Goran S. Milovanović, Phd
DataKolektiv, Owner
Smartocto, Senior Data Scientist

Feedback should be send to goran.milovanovic@datakolektiv.com. These notebook accompanies the BEHAVIORAL DECISION THEORY (BDT) EXPERIMENTAL DESIGN, MODEL ESTIMATION AND SELECTION Data Science Conference 2022 Tech Tutorial, 15. November 2022, 16:30 - 18:00 CET

Setup

# - setup
data_dir <- paste0(getwd(), "/_data/")
meq_data_dir <- paste0(data_dir, "_meq/")
kt92_data_dir <- paste0(data_dir, "_kt92/")

# - libs
library(tidyverse)
library(scales)
library(plotly)
library(snowfall)

Fundamental concepts in Behavioral Decision Theory

Risk aversion and risk attitudes



The risky prospect and the sure thing have the same expected value:

\[E[Risky Prospect] = E[X=100, p=.5;X=0, p=.5] = .5\times100 + .5\times0 = 50\]

\[E[Sure Thing] = 50\]

Why are people risk averse?

Utility Functions

Utility Function of an individual decision maker in choice under risk



Daniel Bernoulli, “Specimen Theoriae Novae de Mensura Sortis”, Comentarii Academiae Scientiarum Imperialis Petroolitanae, Tomus V, 1738, pp. 175-192

Risk aversion follows as a consequences of the characteristics of the decision maker’s utility function.

The power utility function:

\[u(x) = x^\rho\] The decision maker is risk averse for \(0 < \rho < 1\) and risk seeking for \(\rho>1\); risk seeking is not seen as a rational risk attitude.

# - power utility function over gains
x <- seq(1, 100, .1)
rho = .67
ux <- x^rho
ra_frame <- data.frame(value = x,
                       utility = ux)
ggplot(data = ra_frame,
       aes(x = value, y = utility)) + 
  geom_point(size = .25, color = "blue") +
  geom_segment(aes(x = 50, 
                   y = 0, 
                   xend = 50, 
                   yend = 50^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 50^rho, 
                   xend = 50, 
                   yend = 50^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 100, 
                   y = 0, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 100^rho, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 100^rho, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = (100^rho)/2, 
                   xend = 100, 
                   yend = (100^rho)/2),
               colour = "red",
               linetype = "dashed",
               size = .1) + 
  ggtitle("Power utility function, rho=.67, Risk Aversion") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

# - power utility function over gains
x <- seq(1, 100, .1)
rho = 1.2
ux <- x^rho
ra_frame <- data.frame(value = x,
                       utility = ux)
ggplot(data = ra_frame,
       aes(x = value, y = utility)) + 
  geom_point(size = .25, color = "red") +
  geom_segment(aes(x = 50, 
                   y = 0, 
                   xend = 50, 
                   yend = 50^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 50^rho, 
                   xend = 50, 
                   yend = 50^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 100, 
                   y = 0, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 100^rho, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = 100^rho, 
                   xend = 100, 
                   yend = 100^rho),
               colour = "black",
               linetype = "dashed",
               size = .1) + 
  geom_segment(aes(x = 0, 
                   y = (100^rho)/2, 
                   xend = 100, 
                   yend = (100^rho)/2),
               colour = "red",
               linetype = "dashed",
               size = .1) + 
  ggtitle("Power utility function, rho=1.2, Risk Seeking") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Daniel Bernoulli, 1738. People make choices under risk by following the principle of the Maximum Expected Utility (MEU):

\[EU(Prospect) = \sum_{i=1}^{N}p_iu(x_i)\]

The approach was axiomatized in 1947. only by John von Neumann and Oskar Morgenstern in their “Theory of Games and Economic Behavior”, by providing a set of axioms that introduce the preference relation and a proof of a representation theorem showing that any decision maker who obeys the axioms of rational choice is at the same time a EU maximizer (and vice versa). Since then: the von Neumann-Morgenstern utility (or vNM Expected Utility Theory).



Kahneman and Tversky’s Reference-Dependent Utility Function

People are risk seeking the domain of losses.

Example. The “Asian disease” framing problem (Tversky & Kahneman, 1981):

Imagine that the US is preparing for the outbreak of an unusual Asian disease, which is expected to kill 600 people. Two alternative programs to combat the disease have been proposed.

  • Program A: 200 people will be saved (72% of participants expressed a preference for A).
  • Program B: 1/3 probability that 600 people will be saved, and a 2/3 probability that no people will be saved.

This finding illustrates risk aversion in the domain of gains.

  • Program C: 400 people will die (22% opted for C).
  • Program D: 1/3 probability that nobody will die, and a 2/3 probability that 600 people will die.

This finding illustrates risk seeking in the domain of losses.

The Reference-Dependent Utility Function:

# - power reference-dependent utility function over gains and losses
x <- seq(-100, 100, .1)
rho = .67
ux <- ifelse(x>=0, x^rho, -(-x)^rho)
ra_frame <- data.frame(value = x,
                       utility = ux)
ggplot(data = ra_frame,
       aes(x = value, y = utility)) + 
  geom_point(size = .25, color = "blue") + 
  geom_hline(yintercept=0, size = .1, colour = "black", linetype = "dashed") +
  geom_vline(xintercept=0, size = .1, colour = "black", linetype = "dashed") +
  ggtitle("Reference-Dependent Power Utility function, rho=.67") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Loss aversion

Kahneman, Knetch & Thaler experiment, 1991.

Half of the participants in the experimental study receive something for free at the beginning of the session, e.g. a beatiful cup



For those who own the item: place the offer.

$7 and 12 cents

  • For those who do not own the item: place the bid.

2$ and 87 cents

That would be a 2.48 ratio in favor of the offered priced.

We need another correction of the utility function. Enter Loss Aversion, \(\lambda\):

# - power reference-dependent utility function over gains and losses
x <- seq(-100, 100, .1)
rho = .67
lambda = 2.2
ux <- ifelse(x>=0, x^rho, -lambda*(-x)^rho)
ra_frame <- data.frame(value = x,
                       utility = ux)
ggplot(data = ra_frame,
       aes(x = value, y = utility)) + 
  geom_point(size = .25, color = "blue") + 
  geom_hline(yintercept=0, size = .1, colour = "black", linetype = "dashed") +
  geom_vline(xintercept=0, size = .1, colour = "black", linetype = "dashed") +
  ggtitle("Loss Averse Reference-Dependent Power Utility function, \nrho=.67, lambda=2.2") +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

If \(\lambda > 1\), we observe loss aversion; however, if \(0<\lambda<1\), we observe gain seeking, and that happens in empirical settings more often than people think.

But there is more to it.

Probability weighting

The four-fold pattern of risk attitudes (Kahneman & Tversky), e.g.

  • If a risky prospect offers only a 5% chance for EUR 100 and a 95% chance to win nothing – people tend to be risk seeking (i.e. asking for more than EUR 5 in exchange for the lottery), but
  • If a risky prospect offers a 95% chance for EUR 100 and only a 5% chance to win nothing – people tend to be risk averse (i.e. asking less than EUR 95in exchange for the lottery);

and of course we observe the reflection effect in the domain of losses:

  • If a risky prospect offers only a 5% chance for a loss of EUR 100 and a 95% chance to lose nothing – people tend to be risk averse (i.e. offering more than EUR 5 to avoid the risky event), but
  • If a risky prospect offers a 95% chance to lose EUR 100 and only a 5% chance to lose nothing – people tend to be risk seeking (i.e. offering less than EUR 95 to avoid the risky event).

It is like if there was a probability weighting function, w(p) such that \(w(p) > p\) for small \(p\) and \(w(p) < p\) for high \(p\), with an inflection point somewhere. Many functional forms were proposed for \(w(p)\); we will mention only the Prelec’s one-paramater form (1988):

\[w(p)=exp(-(-ln(p))^\gamma)), 0<\gamma<1 \]

# - Prelec's single-parameter probability weighting function
p_weight <- function(p, gamma) {
  wp <- exp(-(-log(p))^gamma)
  return(wp)
} 
p <- seq(0,1,.01)
gamma <- .65
wp <- p_weight(p, gamma)
wp_frame <- data.frame(probability = p,
                       w_p = wp)
ggplot(data = wp_frame,
       aes(x = probability, y = wp)) + 
  geom_path(size = .25, color = "blue", group=1) + 
  ggtitle("Prelec's one-parameter w(p), gamma=.65") +
  geom_segment(aes(x = 0, 
                   y = 0, 
                   xend = 1, 
                   yend = 1),
               colour = "black",
               linetype = "dashed",
               size = .1) +
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Prospect Theory

Finally,

\[u_g(x)=x^{\rho_g}\]

\[u_l(x)=-{\lambda}x^{\rho_l}\]

\[w_g(p)=exp(-(-ln(p_g))^{\gamma_g}))\] \[w_l(p)=exp(-(-ln(p_l))^{\gamma_l}))\]

And then in the simplest case we have

\[PT_G(Prospect) = \sum_{i=1}^{N}w_g(p_i)u_g(x_i)\]

for gains and

\[PT_L(Prospect) = \sum_{i=1}^{N}w_l(p_i)u_l(x_i)\] for losses. For mixed prospects (i.e. lotteries including both gains and losses), we sum up the \(PT()\) for the positive and the negative part:

\[PT_{MIXED}(Prospect) = PT_G(Prospect)+PT_L(Prospect)\]

Measurement and Data





Inspect some Monetary Equivalents (MEq) data (Kahneman & Tversky, 1992):

# - load some MEq experimental data
meq_data <- read.csv(paste0(kt92_data_dir, "kt92.csv"),
                     header = TRUE,
                     check.names = FALSE,
                     row.names = 1,
                     stringsAsFactors = FALSE)
# - percents to probability in meq_data
meq_data$p1 <- meq_data$p1/100
meq_data$p2 <- meq_data$p2/100
print(meq_data)

Estimating the Expected Utility Theory (EUR) model via LS

Start simple: let’s estimate the EUT parameter (there is only one: the power-utility \(\rho\) exponent) via Least Squares.

Utility function

utility_power() is our EUT utility function.

# - power utility function
utility_power <- function(x, rho) {
  u <- x^rho
  return(u)
}

Prediction

A function eut_predict() to predict monetary equivalents from a dataset via EUT:

# - function: EUT predictions
eut_predict <- function(data, rho) {
  
  # - utility functions
  u1 <- ifelse(data$v1 >= 0,
               utility_power(data$v1, rho),
               -utility_power(-data$v1, rho)
               )
  u2 <- ifelse(data$v2 >= 0,
               utility_power(data$v2, rho),
               -utility_power(-data$v2, rho)
               )
  
  # - predictions: utility scale
  predictions <- data$p1*u1 + data$p2*u2
  
  # - predictions: value scale
  predictions <- ifelse(predictions >= 0,
                        predictions^(1/rho),
                        -((-predictions)^(1/rho)))
  
  # - output
  return(predictions)
  
}

NOTE. The predictions: value scale part of the code is very important, because we do not wish to make predictions on the utility scale but rather on the value (MEq) scale. So, let’s not forget that

\[MEq = u(x)^{1/\rho}\] for gains, and

\[MEq = -(u(-x)^{1/\rho})\] for losses.

And test our predictions for \(\rho=.76\):

preds <- eut_predict(data = meq_data, rho = .76)
print(preds)
 [1]    2.4164651   20.0852807   43.5274054   -2.4164651  -20.0852807  -43.5274054
 [7]    1.9414152   16.1367400   40.1705613   68.4868080   93.4735968   -1.9414152
[13]  -16.1367400  -40.1705613  -68.4868080  -93.4735968    0.4671443    9.6658605
[19]   80.3411227  174.1096214  197.3725857   -0.4671443   -9.6658605  -80.3411227
[25] -174.1096214 -197.3725857    0.9342886  394.7451714   -0.9342886 -394.7451714
[31]   54.6116284   73.9745732   94.6469643  -54.6116284  -73.9745732  -94.6469643
[37]   54.3353025   72.4912535   96.8153714  122.7129797  144.4385997  -54.3353025
[43]  -72.4912535  -96.8153714 -122.7129797 -144.4385997  104.5872226  123.4136252
[49]  147.9491464  173.5050929  194.6292955 -104.5872226 -123.4136252 -147.9491464
[55] -173.5050929 -194.6292955

SSE: objective

Now the objective function eut_sse(): it computes the \(SSE\) following the predictive pass of eut_predict()

# - optimize the EUT model via LS
eut_sse <- function(params) {
  rho <- abs(params[1])
  preds <- eut_predict(meq_data, rho)
  sse = sum((meq_data$meq - preds)^2)
  return(sse)
}

Test for \(\rho=.76\):

eut_sse(params=.76)
[1] 7535.473

EUT model optimization

And we want to minimize eut_sse() for the MEq dataset at hand. We will use Nelder-Mead with R optim():

# - random initial value
init_rho <- runif(1, 0, 1)
solution <- optim(par = init_rho, 
                  fn = eut_sse, 
                  method = "Nelder-Mead", 
                  control = list("maxit" = 10e6))
print(paste0("Estimated Rho of ", round(solution$par, 2)))
[1] "Estimated Rho of 0.7"
print(paste0("Convergence check: ", solution$convergence))
[1] "Convergence check: 0"

Now let’s predict the observed MEqs from the estimated parameter:

# - predict from the optimized EUT model
meq_data$eut_meq <- eut_predict(meq_data,
                                rho = solution$par)
print(meq_data)

And plot the predictions:

# - plot predictions
ggplot(data = meq_data,
       aes(x = eut_meq, y = meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle(paste0("EUT, Estimated Rho of ", round(solution$par, 2))) +
  xlab("Predicted MEq") + 
  ylab("Observed MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Let’s run 100 optimizations to check if we are stuck in some local minimum only:

# - many optimization runs
init_rho <- runif(100, 0, 1)

# - run
results <- lapply(init_rho, function(x) {
  
  # - optimization run
  optim_sol <- tryCatch(optim(par = x,
                              fn = eut_sse,
                              method = "Nelder-Mead",
                              control = list("maxit" = 10e6)),
                        error = function(condition) {
                          return(NULL)
                        })
  
  if (!is.null(optim_sol)) {
    
    # - predictions
    preds <- eut_predict(meq_data, rho = optim_sol$par)
    
    return(
      list(rho = optim_sol$par,
           sse = optim_sol$value,
           convergence = optim_sol$convergence,
           predictions = preds)
      )
    
  } else {
      
      return(
      list(rho = NULL,
           sse = NULL,
           convergence = NULL,
           predictions = NULL)
      )
    
  }
  
  
})

# - find best solution
sse_list <- sapply(results, function(x) x$sse)
best_run <- which.min(sse_list)[1]
print(paste0("Check convergence: ", results[[best_run]]$convergence))
[1] "Check convergence: 0"
estimated_rho <- results[[best_run]]$rho
optimal_predictions <- results[[best_run]]$predictions

# - plot predictions
plot_frame <- meq_data
meq_data$eut_meq <- optimal_predictions
ggplot(data = meq_data,
       aes(x = meq, y = eut_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle(paste0("EUT, Estimated Rho of ", round(estimated_rho, 2))) +
  xlab("Predicted MEq") + 
  ylab("Observed MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

EUT model error surface

The EUT model error surface from a sample of 100,000 random parameter values:

# - sample parameters from a range for plotting purposes
sample_parameters <- data.frame(rho = runif(100000, 0, 1))
sample_parameters$sse <- sapply(sample_parameters$rho, eut_sse)
head(sample_parameters)
ggplot(data = sample_parameters,
       aes(x = rho, y = sse)) + 
  geom_line(size = .25, color = "black", group=1) + 
  ggtitle("EUT Model Error Surface ") +
  xlab("Rho") + 
  ylab("SSE") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Estimating the Expected Utility Theory (EUR) model via MLE

Approach to MLE for EUT

For the Maximum-Likelihood Estimation (MLE) of EUT, let’s assume the following:

  • the decision maker computes the \(u(x)\) internally, and the
  • responds with \(u(x)+Normal(\mu=0,\sigma)\), i.e. adding a random noise to their response.

Then we can obtain the likelihood of each MEq observation from \(Normal(response-prediction, \sigma)\), estimating one additional response model parameter \(\sigma\).

The function eut_likelihood returns the negative log-likelihood for a dataset given parameters \(\rho,\sigma\):

# - function: EUT model likelihood
eut_likelihood <- function(data, rho, sigma) {
  
  # - utility functions
  u1 <- ifelse(data$v1 >= 0,
               utility_power(data$v1, rho),
               -utility_power(-data$v1, rho)
               )
  u2 <- ifelse(data$v2 >= 0,
               utility_power(data$v2, rho),
               -utility_power(-data$v2, rho)
               )
  
  # - predictions: utility scale
  predictions <- data$p1*u1 + data$p2*u2
  
  # - predictions: value scale
  predictions <- ifelse(predictions >= 0,
                        predictions^(1/rho),
                        -((-predictions)^(1/rho)))
  
  # - likelihood
  # - assume that the decision maker computes 
  # - the utility of the gamble and its MEQ, and then
  # - responds with an added random noise of Normal(0, sigma)
  neg_loglike <- -sum(
    dnorm(predictions - data$meq, mean = 0, sd = sigma, log = TRUE)
  )

  # - output
  return(neg_loglike)
  
}

Test eut_likelihood for \(\rho=.76\) and \(\sigma=11\):

nll <- eut_likelihood(data = meq_data, rho = .76, sigma = 11)
print(nll)
[1] 216.881

The objective

Introduce the objective eut_mle() function

# - optimize the EUT model via LS
eut_mle <- function(params) {
  rho <- abs(params[1])
  sigma <- abs(params[2])
  nll <- eut_likelihood(data = meq_data, rho, sigma)
  return(nll)
}

Optimize EUT model

And optimize eut_mle() for meq_data:

# - random initial value
init_rho <- runif(1, 0, 1)
init_sigma <- runif(1, 0, 1)
solution <- optim(par = c(init_rho, init_sigma), 
                  fn = eut_mle, 
                  method = "Nelder-Mead", 
                  control = list("maxit" = 10e6))
print(paste0("Estimated Rho of ", round(abs(solution$par[1]), 2)))
[1] "Estimated Rho of 0.7"
print(paste0("Estimated Sigma of ", round(abs(solution$par[2]), 2)))
[1] "Estimated Sigma of 11.53"
print(paste0("Convergence check: ", solution$convergence))
[1] "Convergence check: 0"
# - predict from the optimized EUT model
meq_data$eut_meq <- eut_predict(meq_data,
                                rho = abs(solution$par[1]))

# - plot predictions
ggplot(data = meq_data,
       aes(x = meq, y = eut_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle(paste0("EUT, Estimated Rho of ", round(abs(solution$par), 2))) +
  xlab("Predicted MEq") + 
  ylab("Observed MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Check by 100 optimization runs:

# - many optimization runs
init_pars <- lapply(1:100, function(x) {
  return(
    list(
      init_rho = runif(1, 0, 1),
      init_sigma = runif(1, 0, 1)
    )
  )
})

# - run
results <- lapply(init_pars, function(x) {
  
  init_rho <- x$init_rho
  init_sigma <- x$init_sigma
  
  # - optimization run
  optim_sol <- tryCatch(optim(par = c(init_rho, init_sigma),
                              fn = eut_mle,
                              method = "Nelder-Mead",
                              control = list("maxit" = 10e6)),
                        error = function(condition) {
                          return(NULL)
                        })
  
  if (!is.null(optim_sol)) {
    
    # - predictions
    preds <- eut_predict(meq_data, rho = abs(optim_sol$par[1]))

    return(
      list(rho = optim_sol$par[1],
           sigma = optim_sol$par[2],
           nll = optim_sol$value,
           convergence = optim_sol$convergence,
           predictions = preds)
      )
    
  } else {
      
      return(
      list(rho = NULL,
           sigma = NULL,
           nll = NULL,
           convergence = NULL,
           predictions = NULL)
      )
    
  }
  
  
})

# - find best solution
nll_list <- sapply(results, function(x) x$nll)
best_run <- which.min(nll_list)[1]
print(paste0("Check convergence: ", results[[best_run]]$convergence))
[1] "Check convergence: 0"
estimated_rho <- abs(results[[best_run]]$rho)
print(paste0("Estimated Rho: ", estimated_rho))
[1] "Estimated Rho: 0.699160388762866"
estimated_sigma <- abs(results[[best_run]]$sigma)
print(paste0("Estimated Sigma: ", estimated_sigma))
[1] "Estimated Sigma: 11.5171137364202"
optimal_predictions <- results[[best_run]]$predictions

# - plot predictions
plot_frame <- meq_data
meq_data$eut_meq <- optimal_predictions
ggplot(data = meq_data,
       aes(x = meq, y = eut_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle(paste0("EUT, Estimated Rho of ", round(estimated_rho, 2))) +
  xlab("Predicted MEq") + 
  ylab("Observed MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

The EUT Negative Log-Likelihood surface

# - sample parameters from a range for plotting purposes
sample_parameters <- data.frame(rho = runif(100000, 0, 1.5), 
                                sigma = runif(100000, 0, 15))
sample_parameters$nll <- apply(sample_parameters, 1, function(x) {
    eut_mle(params = c(x[1], x[2]))
})
head(sample_parameters)
plot_ly() %>% 
  add_trace(data = sample_parameters,  
            x = sample_parameters$rho, 
            y = sample_parameters$sigma, 
            z = sample_parameters$nll, 
            type = "mesh3d", 
            intensity = sample_parameters$nll) %>% 
  layout(
    modebar = list(orientation = "v"), 
    title = "The EUT Negative Log-Likehood function",
    scene = list(
      xaxis = list(title = "Rho", range = c(0, 1.5)),
      yaxis = list(title = "Sigma", range = c(0, 15)),
      zaxis = list(title = "Neg-LogLike", range = c(0, 1000))
    )) %>% hide_colorbar()

Estimating the (Cumulative) Prospect Theory (CPT) model via MLE

We will begin with a different, larger data set (Milovanovic, 2013):

# - load some MEq experimental data
meq_data <- read.csv(paste0(meq_data_dir, "es4v1.csv"),
                     header = TRUE,
                     check.names = FALSE,
                     row.names = 1,
                     stringsAsFactors = FALSE)
# - percents to probability in meq_data
meq_data$p1 <- meq_data$p1/100
meq_data$p2 <- meq_data$p2/100
print(meq_data)

The PT Reference-Dependent Power Utility Function

Enters rd_utility_power():

# - reference_dependent power utility function
rd_utility_power <- function(x, rho_g, rho_l, lambda) {
  u <- ifelse(x >= 0, 
              x^rho_g,
              -lambda*((-x)^rho_l))
  return(u)
}

Probability Weighting Function

Enters p_weight()

# - Prelec's single-parameter probability weighting function
# - NOTE: w(p) will be applied over gains and losses separately 
p_weight <- function(p, gamma) {
  
  wp <- exp(-(-log(p))^gamma)
  return(wp)

}

The Prospect Theory Negative Log-Likelihood

PT optimization and predictions

cpt_likelihood() returns the negative log-likelihood for a given dataset:

# - function: CPT model likelihood
cpt_likelihood <- function(data, 
                           rho_g, 
                           rho_l, 
                           lambda, 
                           gamma_gain, 
                           gamma_loss,
                           sigma) {
  
  # - utility functions
  u1 <- rd_utility_power(data$v1, rho_g, rho_l, lambda)
  u2 <- rd_utility_power(data$v2, rho_g, rho_l, lambda)
  
  # - probability weighting
  wp1 <- vector(mode = "numeric", length = length(data$v1))
  ix_gain_1 <- which(data$v1 >= 0)
  wp1[ix_gain_1] <- p_weight(data$p1[ix_gain_1], gamma_gain)
  ix_loss_1 <- which(data$v1 < 0)
  wp1[ix_loss_1] <- p_weight(data$p1[ix_loss_1], gamma_loss)
  wp2 <- 1-wp1
  
  # - predictions: utility scale
  predictions <- wp1*u1 + wp2*u2
  
  # - predictions: value scale
  predictions <- ifelse(predictions >= 0,
                        predictions^(1/rho_g),
                        -(((-predictions)/lambda)^(1/rho_l))
                        )
  
  # - likelihood
  # - assume that the decision maker computes 
  # - the utility of the gamble and its MEQ, and then
  # - responds with an added random noise of Normal(0, sigma)
  neg_loglike <- -sum(
    dnorm(predictions - data$meq, mean = 0, sd = sigma, log = TRUE)
  )

  # - output
  return(neg_loglike)
  
}

Test cpt_likelihood() for a set of parameters:

nll <- cpt_likelihood(data = meq_data, 
                      rho_g = .71, 
                      rho_l = .69, 
                      lambda = 2, 
                      gamma_gain = .67, 
                      gamma_loss = .61, 
                      sigma = .14)
print(nll)
[1] 5752202

And finally minimize cpt_likelihood() for meq_data:

# - optimize the CPT model via LS
cpt_mle <- function(params) {
  rho_g <- abs(params[1])
  rho_l <- abs(params[2])
  lambda <- abs(params[3])
  gamma_gain <- abs(params[4])
  gamma_loss <- abs(params[5])
  sigma <- abs(params[6])
  nll <- cpt_likelihood(data = meq_data, 
                        rho_g, rho_l, lambda,
                        gamma_gain, gamma_loss, sigma)
  return(nll)
}

# - random initial value
init_rho_g <- runif(1, 0, 1)
init_rho_l <- runif(1, 0, 1)
init_lambda <- runif(1, 0, 10)
init_gamma_gain <- runif(1, 0, 10)
init_gamma_loss <- runif(1, 0, 1)
init_sigma <- runif(1, 0, 1)
solution <- optim(par = c(init_rho_g, 
                          init_rho_l,
                          init_lambda,
                          init_gamma_gain,
                          init_gamma_loss,
                          init_sigma), 
                  fn = cpt_mle, 
                  method = "Nelder-Mead", 
                  control = list("maxit" = 10e6))
print(paste0("Estimated Rho for gains of ", round(abs(solution$par[1]), 2)))
[1] "Estimated Rho for gains of 1.14"
print(paste0("Estimated Rho for losses of ", round(abs(solution$par[2]), 2)))
[1] "Estimated Rho for losses of 0.63"
print(paste0("Estimated Lambda (loss aversion) of ", round(abs(solution$par[3]), 2)))
[1] "Estimated Lambda (loss aversion) of 20.98"
print(paste0("Estimated Gamma for gains of ", round(abs(solution$par[4]), 2)))
[1] "Estimated Gamma for gains of 0.77"
print(paste0("Estimated Gamma for losses of ", round(abs(solution$par[5]), 2)))
[1] "Estimated Gamma for losses of 1"
print(paste0("Estimated Sigma of ", round(abs(solution$par[6]), 2)))
[1] "Estimated Sigma of 30.59"
print(paste0("Convergence check: ", solution$convergence))
[1] "Convergence check: 0"

cpt_predict() predicts Monetary Equivalents for a given dataset:

# - function: CPT model predictions
cpt_predict <- function(data,
                        rho_g, rho_l, lambda,
                        gamma_gain, gamma_loss) {
  
  # - utility functions
  u1 <- rd_utility_power(data$v1, rho_g, rho_l, lambda)
  u2 <- rd_utility_power(data$v2, rho_g, rho_l, lambda)
  
  # - probability weighting
  wp1 <- vector(mode = "numeric", length = length(data$v1))
  ix_gain_1 <- which(data$v1 >= 0)
  wp1[ix_gain_1] <- p_weight(data$p1[ix_gain_1], gamma_gain)
  ix_loss_1 <- which(data$v1 < 0)
  wp1[ix_loss_1] <- p_weight(data$p1[ix_loss_1], gamma_loss)
  wp2 <- 1-wp1
  
  # - predictions: utility scale
  predictions <- wp1*u1 + wp2*u2
  
  # - predictions: value scale
  predictions <- ifelse(predictions >= 0,
                        predictions^(1/rho_g),
                        -(((-predictions)/lambda)^(1/rho_l))
                        )
  
  # - output
  return(predictions)
  
}

NOTE. The predictions: value scale part of the code is very important, because we do not wish to make predictions on the utility scale but rather on the value (MEq) scale. So, let’s not forget that under Prospect Theory we have:

\[MEq = u_g(x)^{1/\rho_g}\] while for losßes we have

\[MEq = -((\frac{u_l(-x)}{\lambda})^{1/\rho_l})\]

Test cpt_predict():

# - predict from the optimized CPT model
meq_data$cpt_meq <- cpt_predict(data = meq_data,
                                rho_g = abs(solution$par[1]),
                                rho_l = abs(solution$par[2]),
                                lambda = abs(solution$par[3]),
                                gamma_gain = abs(solution$par[4]),
                                gamma_loss = abs(solution$par[5])
                                )

# - plot predictions
ggplot(data = meq_data,
       aes(x = meq, y = cpt_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle("CPT") +
  xlab("Predicted MEq") + 
  ylab("Observed MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

Run 1,000 optimizations (in parallel, using snowfall):

# - init cloud
sfInit(parallel = TRUE, cpus = 23)
R Version:  R version 4.0.3 (2020-10-10) 
# - export
sfExport("meq_data")
sfExport("cpt_mle")
sfExport("cpt_predict")
sfExport("cpt_likelihood")
sfExport("p_weight")
sfExport("rd_utility_power")

# - many optimization runs
init_pars <- lapply(1:1000, function(x) {
  return(
    list(
      init_rho_g = runif(1, 0, 1),
      init_rho_l = runif(1, 0, 1),
      init_lambda = runif(1, 0, 1),
      init_gamma_gain = runif(1, 0, 1),
      init_gamma_loss = runif(1, 0, 1),
      init_sigma = runif(1, 0, 1)
    )
  )
})


# - run
results <- sfClusterApplyLB(init_pars, function(x) {
  
  init_rho_g <- x$init_rho_g
  init_rho_l = x$init_rho_l
  init_lambda = x$init_lambda
  init_gamma_gain = x$init_gamma_gain
  init_gamma_loss = x$init_gamma_loss
  init_sigma <- x$init_sigma
        
  # - optimization run
  optim_sol <- tryCatch(optim(par = c(init_rho_g,
                                      init_rho_l,
                                      init_lambda,
                                      init_gamma_gain,
                                      init_gamma_loss,
                                      init_sigma),
                              fn = cpt_mle,
                              method = "Nelder-Mead",
                              control = list("maxit" = 1e5,
                                             "reltol" = 1e-14)),
                        error = function(condition) {
                          return(NULL)
                        })
  
  if (!is.null(optim_sol)) {
    
    # - predictions
    preds <- cpt_predict(meq_data, 
                         rho_g = abs(optim_sol$par[1]),
                         rho_l = abs(optim_sol$par[2]),
                         lambda = abs(optim_sol$par[3]),
                         gamma_gain = abs(optim_sol$par[4]),
                         gamma_loss = abs(optim_sol$par[5])
                         )
    
    return(
      list(rho_g = abs(optim_sol$par[1]),
           rho_l = abs(optim_sol$par[2]),
           lambda = abs(optim_sol$par[3]),
           gamma_gain = abs(optim_sol$par[4]),
           gamma_loss = abs(optim_sol$par[5]),
           sigma = abs(optim_sol$par[6]),
           nll = optim_sol$value,
           convergence = optim_sol$convergence,
           predictions = preds)
      )
    
  } else {
      
      return(
      list(rho = NULL,
           sigma = NULL,
           nll = NULL,
           convergence = NULL,
           predictions = NULL)
      )
    
  }
  
  
})

# - stop cluster
sfStop()

# - filter out possible degenerative Nelder-Mead solutions
conv_list <- sapply(results, function(x) x$convergence)
degenerate <- which(conv_list != 0)
nll_list <- nll_list[-degenerate]
# - find best solution
nll_list <- sapply(results, function(x) x$nll)
best_run <- which.min(nll_list)[1]
print(paste0("Check convergence: ", results[[best_run]]$convergence))
[1] "Check convergence: 0"
estimated_rho_g <- results[[best_run]]$rho_g
print(paste0("Estimated Rho for gains: ", round(estimated_rho_g,2)))
[1] "Estimated Rho for gains: 0.99"
estimated_rho_l <- results[[best_run]]$rho_l
print(paste0("Estimated Rho for losses: ", round(estimated_rho_l,2)))
[1] "Estimated Rho for losses: 0.8"
estimated_lambda <- results[[best_run]]$lambda
print(paste0("Estimated Lambda (loss aversion): ", round(estimated_lambda,2)))
[1] "Estimated Lambda (loss aversion): 2.36"
estimated_gamma_gain <- results[[best_run]]$gamma_gain
print(paste0("Estimated Gamma for gains: ", round(estimated_gamma_gain,2)))
[1] "Estimated Gamma for gains: 0.6"
estimated_gamma_loss <- results[[best_run]]$gamma_loss
print(paste0("Estimated Gamma for losses: ", round(estimated_gamma_loss,2)))
[1] "Estimated Gamma for losses: 0.77"
estimated_gamma_sigma <- results[[best_run]]$sigma
print(paste0("Estimated Sigma: ", round(estimated_sigma,2)))
[1] "Estimated Sigma: 11.52"
nll <- results[[best_run]]$nll
print(paste0("Negative LogLikelihood: ", round(nll,2)))
[1] "Negative LogLikelihood: 2128.3"
optimal_predictions <- results[[best_run]]$predictions

# - plot predictions
plot_frame <- meq_data
meq_data$cpt_meq <- optimal_predictions
ggplot(data = meq_data,
       aes(x = meq, y = cpt_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle("CPT") +
  xlab("Observed MEq") + 
  ylab("Predicted MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

PT optimization for the KT92 data set

NOTE. There are no mixed-prospects in the kt92.csv data set:

# - load some MEq experimental data
meq_data <- read.csv(paste0(kt92_data_dir, "kt92.csv"),
                     header = TRUE,
                     check.names = FALSE,
                     row.names = 1,
                     stringsAsFactors = FALSE)
# - percents to probability in meq_data
meq_data$p1 <- meq_data$p1/100
meq_data$p2 <- meq_data$p2/100
print(meq_data)

Run 1,000 optimizations for the KT92 data set:

# - init cloud
sfInit(parallel = TRUE, cpus = 23)

# - export
sfExport("meq_data")
sfExport("cpt_mle")
sfExport("cpt_predict")
sfExport("cpt_likelihood")
sfExport("p_weight")
sfExport("rd_utility_power")

# - many optimization runs
init_pars <- lapply(1:1000, function(x) {
  return(
    list(
      init_rho_g = runif(1, 0, 1),
      init_rho_l = runif(1, 0, 1),
      init_lambda = runif(1, 0, 1),
      init_gamma_gain = runif(1, 0, 1),
      init_gamma_loss = runif(1, 0, 1),
      init_sigma = runif(1, 0, 1)
    )
  )
})


# - run
results <- sfClusterApplyLB(init_pars, function(x) {
  
  init_rho_g <- x$init_rho_g
  init_rho_l = x$init_rho_l
  init_lambda = x$init_lambda
  init_gamma_gain = x$init_gamma_gain
  init_gamma_loss = x$init_gamma_loss
  init_sigma <- x$init_sigma
        
  # - optimization run
  optim_sol <- tryCatch(optim(par = c(init_rho_g,
                                      init_rho_l,
                                      init_lambda,
                                      init_gamma_gain,
                                      init_gamma_loss,
                                      init_sigma),
                              fn = cpt_mle,
                              method = "Nelder-Mead",
                              control = list("maxit" = 1e5,
                                             "reltol" = 1e-14)),
                        error = function(condition) {
                          return(NULL)
                        })
  
  if (!is.null(optim_sol)) {
    
    # - predictions
    preds <- cpt_predict(meq_data, 
                         rho_g = abs(optim_sol$par[1]),
                         rho_l = abs(optim_sol$par[2]),
                         lambda = abs(optim_sol$par[3]),
                         gamma_gain = abs(optim_sol$par[4]),
                         gamma_loss = abs(optim_sol$par[5])
                         )
    
    return(
      list(rho_g = abs(optim_sol$par[1]),
           rho_l = abs(optim_sol$par[2]),
           lambda = abs(optim_sol$par[3]),
           gamma_gain = abs(optim_sol$par[4]),
           gamma_loss = abs(optim_sol$par[5]),
           sigma = abs(optim_sol$par[6]),
           nll = optim_sol$value,
           convergence = optim_sol$convergence,
           predictions = preds)
      )
    
  } else {
      
      return(
      list(rho = NULL,
           sigma = NULL,
           nll = NULL,
           convergence = NULL,
           predictions = NULL)
      )
    
  }
  
  
})

# - stop cluster
sfStop()

# - filter out possible degenerative Nelder-Mead solutions
conv_list <- sapply(results, function(x) x$convergence)
degenerate <- which(conv_list != 0)
nll_list <- nll_list[-degenerate]
# - find best solution
nll_list <- sapply(results, function(x) x$nll)
best_run <- which.min(nll_list)[1]
print(paste0("Check convergence: ", results[[best_run]]$convergence))
[1] "Check convergence: 0"
estimated_rho_g <- results[[best_run]]$rho_g
print(paste0("Estimated Rho for gains: ", round(estimated_rho_g,2)))
[1] "Estimated Rho for gains: 0.62"
estimated_rho_l <- results[[best_run]]$rho_l
print(paste0("Estimated Rho for losses: ", round(estimated_rho_l,2)))
[1] "Estimated Rho for losses: 0.75"
estimated_lambda <- results[[best_run]]$lambda
print(paste0("Estimated Lambda (loss aversion): ", round(estimated_lambda,2)))
[1] "Estimated Lambda (loss aversion): 5.16"
estimated_gamma_gain <- results[[best_run]]$gamma_gain
print(paste0("Estimated Gamma for gains: ", round(estimated_gamma_gain,2)))
[1] "Estimated Gamma for gains: 0.7"
estimated_gamma_loss <- results[[best_run]]$gamma_loss
print(paste0("Estimated Gamma for losses: ", round(estimated_gamma_loss,2)))
[1] "Estimated Gamma for losses: 0.74"
estimated_gamma_sigma <- results[[best_run]]$sigma
print(paste0("Estimated Sigma: ", round(estimated_sigma,2)))
[1] "Estimated Sigma: 11.52"
nll <- results[[best_run]]$nll
print(paste0("Negative LogLikelihood: ", round(nll,2)))
[1] "Negative LogLikelihood: 189.05"
optimal_predictions <- results[[best_run]]$predictions

# - plot predictions
plot_frame <- meq_data
meq_data$cpt_meq <- optimal_predictions
ggplot(data = meq_data,
       aes(x = meq, y = cpt_meq)) + 
  geom_smooth(method = "lm", size = .25, color = "red", se = FALSE) + 
  geom_point(size = 1.5, color = "black") + 
  geom_point(size = 1, color = "white") + 
  ggtitle("CPT") +
  xlab("Observed MEq") + 
  ylab("Predicted MEq") + 
  theme_bw() + 
  theme(panel.border = element_blank()) + 
  theme(plot.title = element_text(hjust=.5, size = 10))

NOTE. \(\lambda\), the Loss Aversion parameter, cannot be estimated from experimental designs that do not encompass mixed lotteries.

Model selection: plan for K-fold CV

# - load some MEq experimental data
meq_data <- read.csv(paste0(meq_data_dir, "es4v1.csv"),
                     header = TRUE,
                     check.names = FALSE,
                     row.names = 1,
                     stringsAsFactors = FALSE)
# - percents to probability in meq_data
meq_data$p1 <- meq_data$p1/100
meq_data$p2 <- meq_data$p2/100
print(meq_data)

Types of lotteries

# - types of lotteries
# - strictly positive
pos_lott <- which(meq_data$v1 > 0 & meq_data$v2 > 0)
print(paste0("There are ", length(pos_lott), " strictly POSITIVE lotteries."))
[1] "There are 90 strictly POSITIVE lotteries."
# - non-negative
nneg_lott <- which ( 
  (meq_data$v1 > 0 & meq_data$v2 == 0) |
    (meq_data$v1 == 0 & meq_data$v2 > 0)
)
print(paste0("There are ", length(nneg_lott), " NON-NEGATIVE lotteries."))
[1] "There are 45 NON-NEGATIVE lotteries."
# - strictly negative
neg_lott <- which(meq_data$v1 < 0 & meq_data$v2 < 0)
print(paste0("There are ", length(neg_lott), " strictly NEGATIVE lotteries."))
[1] "There are 90 strictly NEGATIVE lotteries."
# - non-positive
npos_lott <- which ( 
  (meq_data$v1 < 0 & meq_data$v2 == 0) |
    (meq_data$v1 == 0 & meq_data$v2 < 0)
)
print(paste0("There are ", length(npos_lott), " NON-POSITIVE lotteries."))
[1] "There are 45 NON-POSITIVE lotteries."
# - mixed
mixed_lott <- which ( 
  (meq_data$v1 < 0 & meq_data$v2 > 0) |
    (meq_data$v1 > 0 & meq_data$v2 < 0)
)
print(paste0("There are ", length(mixed_lott), " MIXED lotteries."))
[1] "There are 225 MIXED lotteries."

Let’s enter type to meq_data:

meq_data$type <- apply(meq_data, 1, function(x) {
  if (x[2] > 0 & x[4] > 0) {
    return("p")
  } else if ((x[2] > 0 & x[4] == 0) | 
             (x[2] == 0 & x[4] > 0)) {
    return("nn")
  } else if (x[2] < 0 & x[4] < 0) {
    return("n")
  } else if ((x[2] < 0 & x[4] == 0) |
    (x[2] == 0 & x[4] < 0)) {
    return("np")
  } else {
    return("mixed")
  }
})
table(meq_data$type)

mixed     n    nn    np     p 
  225    90    45    45    90 

You need to make sure that all types of lotteries enter all of your folds in k-fold cross-validation of this model.

Folds plan

meq_data$fold <- 0
types <- unique(meq_data$type)
for (i in 1:length(types)) {
  w <- which(meq_data$type == types[i])
  f_asgn <- rep(1:5, length(w)/5)
  f_asgn <- sample(f_asgn, size = length(f_asgn), replace = FALSE)
  meq_data$fold[w] <- f_asgn
}
print(meq_data)

Check:

table(meq_data$fold, meq_data$type)
   
    mixed  n nn np  p
  1    45 18  9  9 18
  2    45 18  9  9 18
  3    45 18  9  9 18
  4    45 18  9  9 18
  5    45 18  9  9 18

Goran S. Milovanović

DataKolektiv, 2022.

contact:


License: GPLv3 This Notebook is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This Notebook is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this Notebook. If not, see http://www.gnu.org/licenses/.


LS0tCnRpdGxlOiBCRUhBVklPUkFMIERFQ0lTSU9OIFRIRU9SWSAoQkRUKSBFWFBFUklNRU5UQUwgREVTSUdOLCBNT0RFTCBFU1RJTUFUSU9OIEFORCBTRUxFQ1RJT04Kc3VidGl0bGU6IERhdGEgU2NpZW5jZSBDb25mZXJlbmNlIDIwMjIgVGVjaCBUdXRvcmlhbCwgMTUuIE5vdmVtYmVyIDIwMjIuCmFic3RyYWN0OiBXZSBpbnRyb2R1Y2UgdGhlIGZ1bmRhbWVudGFsIGNvbmNlcHRzLCB0eXBpY2FsIGFwcHJvYWNoZXMgdG8gZXhwZXJpbWVudGFsIGRlc2lnbiBhbmQgbWVhc3VyZW1lbnQsIGFuZCBwYXJhbWV0cmljIG1vZGVsIGVzdGltYXRpb24gdmlhIE1MRSBpbiBCZWhhdmlvcmFsIERlY2lzaW9uIFRoZW9yeSAoQkRUKS4gSW4gdGhlIGZvbGxvd2luZyAxLjUgaG91cnMgb2YgdGhpcyB0ZWNoIHR1dG9yaWFsIGluIHRoZSBzY29wZSBvZiBbRFNDIDIwMjJdKGh0dHBzOi8vZGF0YXNjaWNvbmZlcmVuY2UuY29tLykgd2Ugd2lsbCBjb25zaWRlciBvbmx5IGNob2ljZSB1bmRlciByaXNrIChhbmQgbm90IHVuY2VydGFpbnR5KSwgYW5kIHN0dWR5IHR3byBpbXBvcnRhbnQgbW9kZWxzLiBXZSB3aWxsIHJldmlldyBhbmQgcHJvdmlkZSB0b29scyBpbiBSIGZvciB0aGUgTUxFIGVzdGltYXRpb24gb2YgdGhlIChub3JtYXRpdmUpIEV4cGVjdGVkIFV0aWxpdHkgVGhlb3J5IChFVVQsIHZvbiBOZXVtYW5u4oCTTW9yZ2Vuc3Rlcm4pIGFuZCB0aGUgKGRlc2NyaXB0aXZlKSBDdW11bGF0aXZlIFByb3NwZWN0IFRoZW9yeSAoVHZlcnNreS1LYWhuZW1hbikuIFdlIGFwcHJvYWNoIHRoZSBtb2RlbCBzZWxlY3Rpb24gaW4gQkRUIHZpYSBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiwgbW9yZSBjaGFyYWN0ZXJpc3RpYyBvZiB0aGUgTUwgY29tbXVuaXR5IGFuZCByYXJlbHkgdXNlZCBpbiBlbXBpcmljYWwgYXBwcm9hY2hlcyBpbiBtaWNyb2Vjb25vbWljcywgYmVoYXZpb3JhbCBlY29ub21pY3MsIGFuZCBjb2duaXRpdmUgcHN5Y2hvbG9neS4gSG93ZXZlciwgdHlwaWNhbCwgb3JkaW5hcnkgYXBwcm9hY2hlcyB0byBrLWZvbGQgQ1YgZmFpbCBiZWNhdXNlIG9mIHRoZSBkZWVwIGRlcGVuZGVuY2UgYmV0d2VlbiB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiAoaS5lLiB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhIHNldCkgYW5kIG1vZGVsIGVzdGltYXRpb24gcHJvY2VkdXJlcy4gVW5kZXJzdGFuZGluZyB0aGUgZnVuZGFtZW50cyBvZiBCRFQgYW5kIGJlaW5nIGFibGUgdG8gZXN0aW1hdGUgcGFyYW1ldHJpYyBtb2RlbHMgb2YgY2hvaWNlIHVuZGVyIHJpc2sgaXMgYSBwcmVyZXF1aXNpdGUgZm9yIHRoZSBkZXNpZ24gb2YgYW55IGJlaGF2aW9yYWwgaW50ZXJ2ZW50aW9uLCBhbiBhcHJvYWNoIHRoYXQgZ2FpbmVkIHNvIG11Y2ggcG9wdWxhcml0eSBhbmQgYXR0cmFjdGVkIHNvIG11Y2ggYXR0ZW50aW9uIGZvbGxvd2luZyB0aGUgcmVjb2duaXRpb24gb2YgdGhlIGNlbnRyYWwgaW1wb3J0YW5jZSBvZiBiZWhhdmlvcmFsIGVjb25vbWljcyAoZS5nIEthaG5lbWFuJ3MgKDIwMDIpIGFuZCBUaGFsZXIncyAoMjAxNykgTm9iZWwgTWVtb3JpYWwgUHJpemUgaW4gRWNvbm9taWMgU2NpZW5jZXMpIHRvIHRoZSBzdHVkeSBvZiBodW1hbiBkZWNpc2lvbiBtYWtpbmcuIEJleW9uZCwgY2hhcmFjdGVyaXphdGlvbnMgb2YgaW5kaXZpZHVhbCBkZWNpc2lvbiBtYWtlcnMgYnkgZXN0aW1hdGVkIEJURCBtb2RlbCBwYXJhbWV0ZXJzIHByb3ZpZGUgbmV3IHNldHMgb2YgZmVhdHVyZXMgZm9yIGFueSBmdXJ0aGVyIHJpc2sgbW9kZWxpbmcuICAKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGhlbWU6IHNwYWNlbGFiCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCjxicj4KPHAgYWxpZ249ImNlbnRlciI+CiAgICA8aW1nIHNyYz0iX2ltZy9nc21fcm91bmRfMTc1cHgucG5nIj4KPC9wPgoKPGNlbnRlcj5bR29yYW4gUy4gTWlsb3Zhbm92acSHLCBQaGRdKGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9pbi9nbWlsb3Zhbm92aWMvKTwvY2VudGVyPiAKCjxjZW50ZXI+W0RhdGFLb2xla3Rpdl0oaHR0cHM6Ly93d3cuZGF0YWtvbGVrdGl2LmNvbS8pLCBPd25lcjwvY2VudGVyPgoKPGNlbnRlcj5bU21hcnRvY3RvXShodHRwczovL3NtYXJ0b2N0by5jb20vKSwgU2VuaW9yIERhdGEgU2NpZW50aXN0PC9jZW50ZXI+IAoKCioqKgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gW2dvcmFuLm1pbG92YW5vdmljQGRhdGFrb2xla3Rpdi5jb21dKG1haWx0bzpnb3Jhbi5taWxvdmFub3ZpY0BkYXRha29sZWt0aXYuY29tKS4gClRoZXNlIG5vdGVib29rIGFjY29tcGFuaWVzIHRoZSAqKkJFSEFWSU9SQUwgREVDSVNJT04gVEhFT1JZIChCRFQpIEVYUEVSSU1FTlRBTCBERVNJR04sIE1PREVMIEVTVElNQVRJT04gQU5EIFNFTEVDVElPTiBEYXRhIFNjaWVuY2UgQ29uZmVyZW5jZSAyMDIyIFRlY2ggVHV0b3JpYWwqKiwgMTUuIE5vdmVtYmVyIDIwMjIsIDE2OjMwIC0gMTg6MDAgQ0VUCgojIyBTZXR1cAoKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSJoaWRlIiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyAtIHNldHVwCmRhdGFfZGlyIDwtIHBhc3RlMChnZXR3ZCgpLCAiL19kYXRhLyIpCm1lcV9kYXRhX2RpciA8LSBwYXN0ZTAoZGF0YV9kaXIsICJfbWVxLyIpCmt0OTJfZGF0YV9kaXIgPC0gcGFzdGUwKGRhdGFfZGlyLCAiX2t0OTIvIikKCiMgLSBsaWJzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkoc25vd2ZhbGwpCmBgYAoKIyMgRnVuZGFtZW50YWwgY29uY2VwdHMgaW4gQmVoYXZpb3JhbCBEZWNpc2lvbiBUaGVvcnkKCiMjIyBSaXNrIGF2ZXJzaW9uIGFuZCByaXNrIGF0dGl0dWRlcwoKPGJyPgo8cCBhbGlnbj0iY2VudGVyIj4KICAgIDxpbWcgc3JjPSJfaW1nL2ZpZzFfcmlza19hdmVyc2lvbi5wbmciPgo8L3A+Cjxicj4KClRoZSByaXNreSBwcm9zcGVjdCBhbmQgdGhlIHN1cmUgdGhpbmcgaGF2ZSB0aGUgc2FtZSBleHBlY3RlZCB2YWx1ZToKCiQkRVtSaXNreSBQcm9zcGVjdF0gPSBFW1g9MTAwLCBwPS41O1g9MCwgcD0uNV0gPSAuNVx0aW1lczEwMCArIC41XHRpbWVzMCA9IDUwJCQKCiQkRVtTdXJlIFRoaW5nXSA9IDUwJCQKCldoeSBhcmUgcGVvcGxlIHJpc2sgYXZlcnNlPwoKIyMjIFV0aWxpdHkgRnVuY3Rpb25zCgojIyMjIFV0aWxpdHkgRnVuY3Rpb24gb2YgYW4gaW5kaXZpZHVhbCBkZWNpc2lvbiBtYWtlciBpbiBjaG9pY2UgdW5kZXIgcmlzawoKPGJyPgo8cCBhbGlnbj0ibGVmdCI+CiAgICA8aW1nIHNyYz0iX2ltZy9EYW5pZWxfQmVybm91bGxpLmpwZyI+CjwvcD4KPGJyPgoKRGFuaWVsIEJlcm5vdWxsaSwgIipTcGVjaW1lbiBUaGVvcmlhZSBOb3ZhZSBkZSBNZW5zdXJhIFNvcnRpcyoiLCAqQ29tZW50YXJpaSBBY2FkZW1pYWUgU2NpZW50aWFydW0gSW1wZXJpYWxpcyBQZXRyb29saXRhbmFlKiwgVG9tdXMgViwgMTczOCwgcHAuIDE3NS0xOTIKClJpc2sgYXZlcnNpb24gZm9sbG93cyBhcyBhIGNvbnNlcXVlbmNlcyBvZiB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBkZWNpc2lvbiBtYWtlcidzIHV0aWxpdHkgZnVuY3Rpb24uCgpUaGUgcG93ZXIgdXRpbGl0eSBmdW5jdGlvbjoKCiQkdSh4KSA9IHheXHJobyQkClRoZSBkZWNpc2lvbiBtYWtlciBpcyAqKnJpc2sgYXZlcnNlKiogZm9yICQwIDwgXHJobyA8IDEkIGFuZCAqKnJpc2sgc2Vla2luZyoqIGZvciAkXHJobz4xJDsgcmlzayBzZWVraW5nIGlzIG5vdCBzZWVuIGFzIGEgcmF0aW9uYWwgcmlzayBhdHRpdHVkZS4KCgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NS41fQojIC0gcG93ZXIgdXRpbGl0eSBmdW5jdGlvbiBvdmVyIGdhaW5zCnggPC0gc2VxKDEsIDEwMCwgLjEpCnJobyA9IC42Nwp1eCA8LSB4XnJobwpyYV9mcmFtZSA8LSBkYXRhLmZyYW1lKHZhbHVlID0geCwKICAgICAgICAgICAgICAgICAgICAgICB1dGlsaXR5ID0gdXgpCmdncGxvdChkYXRhID0gcmFfZnJhbWUsCiAgICAgICBhZXMoeCA9IHZhbHVlLCB5ID0gdXRpbGl0eSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gLjI1LCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IDUwLCAKICAgICAgICAgICAgICAgICAgIHkgPSAwLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSA1MCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gNTBecmhvKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgc2l6ZSA9IC4xKSArIAogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIAogICAgICAgICAgICAgICAgICAgeSA9IDUwXnJobywgCiAgICAgICAgICAgICAgICAgICB4ZW5kID0gNTAsIAogICAgICAgICAgICAgICAgICAgeWVuZCA9IDUwXnJobyksCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAuMSkgKyAKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAxMDAsIAogICAgICAgICAgICAgICAgICAgeSA9IDAsIAogICAgICAgICAgICAgICAgICAgeGVuZCA9IDEwMCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gMTAwXnJobyksCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAuMSkgKyAKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCAKICAgICAgICAgICAgICAgICAgIHkgPSAxMDBecmhvLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgeWVuZCA9IDEwMF5yaG8pLAogICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgICAgICAgICAgICBzaXplID0gLjEpICsgCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgCiAgICAgICAgICAgICAgICAgICB5ID0gMTAwXnJobywgCiAgICAgICAgICAgICAgICAgICB4ZW5kID0gMTAwLCAKICAgICAgICAgICAgICAgICAgIHllbmQgPSAxMDBecmhvKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgc2l6ZSA9IC4xKSArIAogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIAogICAgICAgICAgICAgICAgICAgeSA9ICgxMDBecmhvKS8yLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgeWVuZCA9ICgxMDBecmhvKS8yKSwKICAgICAgICAgICAgICAgY29sb3VyID0gInJlZCIsCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAuMSkgKyAKICBnZ3RpdGxlKCJQb3dlciB1dGlsaXR5IGZ1bmN0aW9uLCByaG89LjY3LCBSaXNrIEF2ZXJzaW9uIikgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKYGBgCgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NS41fQojIC0gcG93ZXIgdXRpbGl0eSBmdW5jdGlvbiBvdmVyIGdhaW5zCnggPC0gc2VxKDEsIDEwMCwgLjEpCnJobyA9IDEuMgp1eCA8LSB4XnJobwpyYV9mcmFtZSA8LSBkYXRhLmZyYW1lKHZhbHVlID0geCwKICAgICAgICAgICAgICAgICAgICAgICB1dGlsaXR5ID0gdXgpCmdncGxvdChkYXRhID0gcmFfZnJhbWUsCiAgICAgICBhZXMoeCA9IHZhbHVlLCB5ID0gdXRpbGl0eSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gLjI1LCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gNTAsIAogICAgICAgICAgICAgICAgICAgeSA9IDAsIAogICAgICAgICAgICAgICAgICAgeGVuZCA9IDUwLCAKICAgICAgICAgICAgICAgICAgIHllbmQgPSA1MF5yaG8pLAogICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgICAgICAgICAgICBzaXplID0gLjEpICsgCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgCiAgICAgICAgICAgICAgICAgICB5ID0gNTBecmhvLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSA1MCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gNTBecmhvKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgc2l6ZSA9IC4xKSArIAogIGdlb21fc2VnbWVudChhZXMoeCA9IDEwMCwgCiAgICAgICAgICAgICAgICAgICB5ID0gMCwgCiAgICAgICAgICAgICAgICAgICB4ZW5kID0gMTAwLCAKICAgICAgICAgICAgICAgICAgIHllbmQgPSAxMDBecmhvKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgc2l6ZSA9IC4xKSArIAogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIAogICAgICAgICAgICAgICAgICAgeSA9IDEwMF5yaG8sIAogICAgICAgICAgICAgICAgICAgeGVuZCA9IDEwMCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gMTAwXnJobyksCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAuMSkgKyAKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCAKICAgICAgICAgICAgICAgICAgIHkgPSAxMDBecmhvLCAKICAgICAgICAgICAgICAgICAgIHhlbmQgPSAxMDAsIAogICAgICAgICAgICAgICAgICAgeWVuZCA9IDEwMF5yaG8pLAogICAgICAgICAgICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgICAgICAgICAgICBzaXplID0gLjEpICsgCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgCiAgICAgICAgICAgICAgICAgICB5ID0gKDEwMF5yaG8pLzIsIAogICAgICAgICAgICAgICAgICAgeGVuZCA9IDEwMCwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gKDEwMF5yaG8pLzIpLAogICAgICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwKICAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwKICAgICAgICAgICAgICAgc2l6ZSA9IC4xKSArIAogIGdndGl0bGUoIlBvd2VyIHV0aWxpdHkgZnVuY3Rpb24sIHJobz0xLjIsIFJpc2sgU2Vla2luZyIpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKRGFuaWVsIEJlcm5vdWxsaSwgMTczOC4gUGVvcGxlIG1ha2UgY2hvaWNlcyB1bmRlciByaXNrIGJ5IGZvbGxvd2luZyB0aGUgcHJpbmNpcGxlIG9mIHRoZSAqKk1heGltdW0gRXhwZWN0ZWQgVXRpbGl0eSAoTUVVKSoqOgoKJCRFVShQcm9zcGVjdCkgPSBcc3VtX3tpPTF9XntOfXBfaXUoeF9pKSQkCgpUaGUgYXBwcm9hY2ggd2FzIGF4aW9tYXRpemVkIGluIDE5NDcuIG9ubHkgYnkgSm9obiB2b24gTmV1bWFubiBhbmQgT3NrYXIgTW9yZ2Vuc3Rlcm4gaW4gdGhlaXIgIipUaGVvcnkgb2YgR2FtZXMgYW5kIEVjb25vbWljIEJlaGF2aW9yKiIsIGJ5IHByb3ZpZGluZyBhIHNldCBvZiBheGlvbXMgdGhhdCBpbnRyb2R1Y2UgdGhlICpwcmVmZXJlbmNlIHJlbGF0aW9uKiBhbmQgYSBwcm9vZiBvZiBhIHJlcHJlc2VudGF0aW9uIHRoZW9yZW0gc2hvd2luZyB0aGF0IGFueSBkZWNpc2lvbiBtYWtlciB3aG8gb2JleXMgdGhlIGF4aW9tcyBvZiByYXRpb25hbCBjaG9pY2UgaXMgYXQgdGhlIHNhbWUgdGltZSBhIEVVIG1heGltaXplciAoYW5kICp2aWNlIHZlcnNhKikuIFNpbmNlIHRoZW46IHRoZSAqdm9uIE5ldW1hbm4tTW9yZ2Vuc3Rlcm4gdXRpbGl0eSogKG9yICp2Tk0gRXhwZWN0ZWQgVXRpbGl0eSBUaGVvcnkpKi4KCjxicj4KPHAgYWxpZ249ImxlZnQiPgogICAgPGltZyBzcmM9Il9pbWcvVk5NLVRHRUIuanBnIj4KPC9wPgo8YnI+CgojIyMjIEthaG5lbWFuIGFuZCBUdmVyc2t5J3MgUmVmZXJlbmNlLURlcGVuZGVudCBVdGlsaXR5IEZ1bmN0aW9uCgoqKlBlb3BsZSBhcmUgcmlzayBzZWVraW5nIHRoZSBkb21haW4gb2YgbG9zc2VzLioqCgpFeGFtcGxlLiBUaGUgIkFzaWFuIGRpc2Vhc2UiIGZyYW1pbmcgcHJvYmxlbSAoVHZlcnNreSAmIEthaG5lbWFuLCAxOTgxKToKCj4gSW1hZ2luZSB0aGF0IHRoZSBVUyBpcyBwcmVwYXJpbmcgZm9yIHRoZSBvdXRicmVhayBvZiBhbiB1bnVzdWFsIEFzaWFuIGRpc2Vhc2UsIHdoaWNoIGlzIGV4cGVjdGVkIHRvIGtpbGwgNjAwIHBlb3BsZS4gVHdvIGFsdGVybmF0aXZlIHByb2dyYW1zIHRvIGNvbWJhdCB0aGUgZGlzZWFzZSBoYXZlIGJlZW4gcHJvcG9zZWQuCgotIFByb2dyYW0gQTogMjAwIHBlb3BsZSB3aWxsIGJlIHNhdmVkICg3MiUgb2YgcGFydGljaXBhbnRzIGV4cHJlc3NlZCBhIHByZWZlcmVuY2UgZm9yIEEpLgotIFByb2dyYW0gQjogMS8zIHByb2JhYmlsaXR5IHRoYXQgNjAwIHBlb3BsZSB3aWxsIGJlIHNhdmVkLCBhbmQgYSAyLzMgcHJvYmFiaWxpdHkgdGhhdCBubyBwZW9wbGUgd2lsbCBiZSBzYXZlZC4KClRoaXMgZmluZGluZyBpbGx1c3RyYXRlcyByaXNrIGF2ZXJzaW9uIGluIHRoZSBkb21haW4gb2YgZ2FpbnMuCgotIFByb2dyYW0gQzogNDAwIHBlb3BsZSB3aWxsIGRpZSAoMjIlIG9wdGVkIGZvciBDKS4KLSBQcm9ncmFtIEQ6IDEvMyBwcm9iYWJpbGl0eSB0aGF0IG5vYm9keSB3aWxsIGRpZSwgYW5kIGEgMi8zIHByb2JhYmlsaXR5IHRoYXQgNjAwIHBlb3BsZSB3aWxsIGRpZS4KClRoaXMgZmluZGluZyBpbGx1c3RyYXRlcyAqcmlzayBzZWVraW5nIGluIHRoZSBkb21haW4gb2YgbG9zc2VzKi4KClRoZSBSZWZlcmVuY2UtRGVwZW5kZW50IFV0aWxpdHkgRnVuY3Rpb246CgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NS41fQojIC0gcG93ZXIgcmVmZXJlbmNlLWRlcGVuZGVudCB1dGlsaXR5IGZ1bmN0aW9uIG92ZXIgZ2FpbnMgYW5kIGxvc3Nlcwp4IDwtIHNlcSgtMTAwLCAxMDAsIC4xKQpyaG8gPSAuNjcKdXggPC0gaWZlbHNlKHg+PTAsIHhecmhvLCAtKC14KV5yaG8pCnJhX2ZyYW1lIDwtIGRhdGEuZnJhbWUodmFsdWUgPSB4LAogICAgICAgICAgICAgICAgICAgICAgIHV0aWxpdHkgPSB1eCkKZ2dwbG90KGRhdGEgPSByYV9mcmFtZSwKICAgICAgIGFlcyh4ID0gdmFsdWUsIHkgPSB1dGlsaXR5KSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAuMjUsIGNvbG9yID0gImJsdWUiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdD0wLCBzaXplID0gLjEsIGNvbG91ciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwgc2l6ZSA9IC4xLCBjb2xvdXIgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2d0aXRsZSgiUmVmZXJlbmNlLURlcGVuZGVudCBQb3dlciBVdGlsaXR5IGZ1bmN0aW9uLCByaG89LjY3IikgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKYGBgCgojIyMgTG9zcyBhdmVyc2lvbgoKS2FobmVtYW4sIEtuZXRjaCAmIFRoYWxlciBleHBlcmltZW50LCAxOTkxLgoKSGFsZiBvZiB0aGUgcGFydGljaXBhbnRzIGluIHRoZSBleHBlcmltZW50YWwgc3R1ZHkgcmVjZWl2ZSBzb21ldGhpbmcgZm9yIGZyZWUgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2Vzc2lvbiwgZS5nLiBhIGJlYXRpZnVsIGN1cAoKPGJyPgo8cCBhbGlnbj0iY2VudGVyIj4KICAgIDxpbWcgc3JjPSJfaW1nL2N1cC5qcGciPgo8L3A+Cjxicj4KCkZvciB0aG9zZSB3aG8gb3duIHRoZSBpdGVtOiAqKnBsYWNlIHRoZSBvZmZlcioqLgoKPHAgc3R5bGU9ImNvbG9yOmJsYWNrO2ZvbnQtc2l6ZToxOHB4OyI+JDcgYW5kIDEyIGNlbnRzPC9wPiAgCgotIEZvciB0aG9zZSB3aG8gZG8gbm90IG93biB0aGUgaXRlbTogKipwbGFjZSB0aGUgYmlkKiouCgo8cCBzdHlsZT0iY29sb3I6YmxhY2s7Zm9udC1zaXplOjE4cHg7Ij4yJCBhbmQgODcgY2VudHM8L3A+ICAKClRoYXQgd291bGQgYmUgYSBgciByb3VuZCg3LjEyLzIuODcsMilgIHJhdGlvIGluIGZhdm9yIG9mIHRoZSBvZmZlcmVkIHByaWNlZC4KCldlIG5lZWQgYW5vdGhlciBjb3JyZWN0aW9uIG9mIHRoZSB1dGlsaXR5IGZ1bmN0aW9uLiBFbnRlciBMb3NzIEF2ZXJzaW9uLCAkXGxhbWJkYSQ6CgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NS41fQojIC0gcG93ZXIgcmVmZXJlbmNlLWRlcGVuZGVudCB1dGlsaXR5IGZ1bmN0aW9uIG92ZXIgZ2FpbnMgYW5kIGxvc3Nlcwp4IDwtIHNlcSgtMTAwLCAxMDAsIC4xKQpyaG8gPSAuNjcKbGFtYmRhID0gMi4yCnV4IDwtIGlmZWxzZSh4Pj0wLCB4XnJobywgLWxhbWJkYSooLXgpXnJobykKcmFfZnJhbWUgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IHgsCiAgICAgICAgICAgICAgICAgICAgICAgdXRpbGl0eSA9IHV4KQpnZ3Bsb3QoZGF0YSA9IHJhX2ZyYW1lLAogICAgICAgYWVzKHggPSB2YWx1ZSwgeSA9IHV0aWxpdHkpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IC4yNSwgY29sb3IgPSAiYmx1ZSIpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIHNpemUgPSAuMSwgY29sb3VyID0gImJsYWNrIiwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0wLCBzaXplID0gLjEsIGNvbG91ciA9ICJibGFjayIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZ3RpdGxlKCJMb3NzIEF2ZXJzZSBSZWZlcmVuY2UtRGVwZW5kZW50IFBvd2VyIFV0aWxpdHkgZnVuY3Rpb24sIFxucmhvPS42NywgbGFtYmRhPTIuMiIpICsKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKSWYgJFxsYW1iZGEgPiAxJCwgd2Ugb2JzZXJ2ZSBsb3NzIGF2ZXJzaW9uOyBob3dldmVyLCBpZiAkMDxcbGFtYmRhPDEkLCB3ZSBvYnNlcnZlICoqZ2FpbiBzZWVraW5nKiosIGFuZCB0aGF0IGhhcHBlbnMgaW4gZW1waXJpY2FsIHNldHRpbmdzIG1vcmUgb2Z0ZW4gdGhhbiBwZW9wbGUgdGhpbmsuCgpCdXQgdGhlcmUgaXMgbW9yZSB0byBpdC4KCiMjIyBQcm9iYWJpbGl0eSB3ZWlnaHRpbmcKClRoZSAqKmZvdXItZm9sZCBwYXR0ZXJuIG9mIHJpc2sgYXR0aXR1ZGVzKiogKEthaG5lbWFuICYgVHZlcnNreSksIGUuZy4KCi0gSWYgYSByaXNreSBwcm9zcGVjdCBvZmZlcnMgb25seSBhICoqNSUqKiBjaGFuY2UgZm9yIEVVUiAxMDAgYW5kIGEgOTUlIGNoYW5jZSB0byB3aW4gbm90aGluZyDigJMgcGVvcGxlIHRlbmQgdG8gYmUgKipyaXNrIHNlZWtpbmcqKiAoaS5lLiBhc2tpbmcgZm9yICoqbW9yZSB0aGFuIEVVUiA1KiogaW4gZXhjaGFuZ2UgZm9yIHRoZSBsb3R0ZXJ5KSwgKmJ1dCoKLSBJZiBhIHJpc2t5IHByb3NwZWN0IG9mZmVycyBhICoqOTUlKiogY2hhbmNlIGZvciBFVVIgMTAwIGFuZCBvbmx5IGEgNSUgY2hhbmNlIHRvIHdpbiBub3RoaW5nIOKAkyBwZW9wbGUgdGVuZCB0byBiZSAqKnJpc2sgYXZlcnNlKiogKGkuZS4gYXNraW5nICoqbGVzcyB0aGFuIEVVUiA5NSoqaW4gZXhjaGFuZ2UgZm9yIHRoZSBsb3R0ZXJ5KTsKCmFuZCBvZiBjb3Vyc2Ugd2Ugb2JzZXJ2ZSB0aGUgcmVmbGVjdGlvbiBlZmZlY3QgaW4gdGhlIGRvbWFpbiBvZiBsb3NzZXM6CgotIElmIGEgcmlza3kgcHJvc3BlY3Qgb2ZmZXJzIG9ubHkgYSAqKjUlKiogY2hhbmNlIGZvciBhIGxvc3Mgb2YgRVVSIDEwMCBhbmQgYSA5NSUgY2hhbmNlIHRvIGxvc2Ugbm90aGluZyDigJMgcGVvcGxlIHRlbmQgdG8gYmUgKipyaXNrIGF2ZXJzZSoqIChpLmUuIG9mZmVyaW5nICoqbW9yZSB0aGFuIEVVUiA1KiogdG8gYXZvaWQgdGhlIHJpc2t5IGV2ZW50KSwgKmJ1dCoKLSBJZiBhIHJpc2t5IHByb3NwZWN0IG9mZmVycyBhICoqOTUlKiogY2hhbmNlIHRvIGxvc2UgRVVSIDEwMCBhbmQgb25seSBhIDUlIGNoYW5jZSB0byBsb3NlIG5vdGhpbmcg4oCTIHBlb3BsZSB0ZW5kIHRvIGJlICoqcmlzayBzZWVraW5nKiogKGkuZS4gb2ZmZXJpbmcgKipsZXNzIHRoYW4gRVVSIDk1KiogdG8gYXZvaWQgdGhlIHJpc2t5IGV2ZW50KS4KCkl0IGlzIGxpa2UgaWYgdGhlcmUgd2FzIGEgKipwcm9iYWJpbGl0eSB3ZWlnaHRpbmcgZnVuY3Rpb24sIHcocCkqKiBzdWNoIHRoYXQgJHcocCkgPiBwJCBmb3Igc21hbGwgJHAkIGFuZCAkdyhwKSA8IHAkIGZvciBoaWdoICRwJCwgd2l0aCBhbiBpbmZsZWN0aW9uIHBvaW50IHNvbWV3aGVyZS4gTWFueSBmdW5jdGlvbmFsIGZvcm1zIHdlcmUgcHJvcG9zZWQgZm9yICR3KHApJDsgd2Ugd2lsbCBtZW50aW9uIG9ubHkgdGhlIFByZWxlYydzIG9uZS1wYXJhbWF0ZXIgZm9ybSAoMTk4OCk6CgokJHcocCk9ZXhwKC0oLWxuKHApKV5cZ2FtbWEpKSwgMDxcZ2FtbWE8MSAkJAoKYGBge3IgZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTUuNX0KIyAtIFByZWxlYydzIHNpbmdsZS1wYXJhbWV0ZXIgcHJvYmFiaWxpdHkgd2VpZ2h0aW5nIGZ1bmN0aW9uCnBfd2VpZ2h0IDwtIGZ1bmN0aW9uKHAsIGdhbW1hKSB7CiAgd3AgPC0gZXhwKC0oLWxvZyhwKSleZ2FtbWEpCiAgcmV0dXJuKHdwKQp9IApwIDwtIHNlcSgwLDEsLjAxKQpnYW1tYSA8LSAuNjUKd3AgPC0gcF93ZWlnaHQocCwgZ2FtbWEpCndwX2ZyYW1lIDwtIGRhdGEuZnJhbWUocHJvYmFiaWxpdHkgPSBwLAogICAgICAgICAgICAgICAgICAgICAgIHdfcCA9IHdwKQpnZ3Bsb3QoZGF0YSA9IHdwX2ZyYW1lLAogICAgICAgYWVzKHggPSBwcm9iYWJpbGl0eSwgeSA9IHdwKSkgKyAKICBnZW9tX3BhdGgoc2l6ZSA9IC4yNSwgY29sb3IgPSAiYmx1ZSIsIGdyb3VwPTEpICsgCiAgZ2d0aXRsZSgiUHJlbGVjJ3Mgb25lLXBhcmFtZXRlciB3KHApLCBnYW1tYT0uNjUiKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgCiAgICAgICAgICAgICAgICAgICB5ID0gMCwgCiAgICAgICAgICAgICAgICAgICB4ZW5kID0gMSwgCiAgICAgICAgICAgICAgICAgICB5ZW5kID0gMSksCiAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgIHNpemUgPSAuMSkgKwogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKYGBgCgojIyMgUHJvc3BlY3QgVGhlb3J5CgpGaW5hbGx5LAoKJCR1X2coeCk9eF57XHJob19nfSQkCgokJHVfbCh4KT0te1xsYW1iZGF9eF57XHJob19sfSQkCgokJHdfZyhwKT1leHAoLSgtbG4ocF9nKSlee1xnYW1tYV9nfSkpJCQKJCR3X2wocCk9ZXhwKC0oLWxuKHBfbCkpXntcZ2FtbWFfbH0pKSQkCgpBbmQgdGhlbiBpbiB0aGUgc2ltcGxlc3QgY2FzZSB3ZSBoYXZlCgokJFBUX0coUHJvc3BlY3QpID0gXHN1bV97aT0xfV57Tn13X2cocF9pKXVfZyh4X2kpJCQKCmZvciBnYWlucyBhbmQgCgokJFBUX0woUHJvc3BlY3QpID0gXHN1bV97aT0xfV57Tn13X2wocF9pKXVfbCh4X2kpJCQKZm9yIGxvc3Nlcy4gRm9yIG1peGVkIHByb3NwZWN0cyAoaS5lLiBsb3R0ZXJpZXMgaW5jbHVkaW5nIGJvdGggZ2FpbnMgYW5kIGxvc3NlcyksIHdlIHN1bSB1cCB0aGUgJFBUKCkkIGZvciB0aGUgcG9zaXRpdmUgYW5kIHRoZSBuZWdhdGl2ZSBwYXJ0OgoKJCRQVF97TUlYRUR9KFByb3NwZWN0KSA9IFBUX0coUHJvc3BlY3QpK1BUX0woUHJvc3BlY3QpJCQKCiMjIE1lYXN1cmVtZW50IGFuZCBEYXRhCgo8YnI+CjxwIGFsaWduPSJjZW50ZXIiPgogICAgPGltZyBzcmM9Il9pbWcvZmlnMl9tZXFfbWVhc3VyZW1lbnQucG5nIj4KPC9wPgo8YnI+Cgo8YnI+CjxwIGFsaWduPSJjZW50ZXIiPgogICAgPGltZyBzcmM9Il9pbWcvZmlnM19jaG9pY2VfZXhwZXJpbWVudC5wbmciPgo8L3A+Cjxicj4KCkluc3BlY3Qgc29tZSBNb25ldGFyeSBFcXVpdmFsZW50cyAoTUVxKSBkYXRhIChLYWhuZW1hbiAmIFR2ZXJza3ksIDE5OTIpOgoKYGBge3IgZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSBsb2FkIHNvbWUgTUVxIGV4cGVyaW1lbnRhbCBkYXRhCm1lcV9kYXRhIDwtIHJlYWQuY3N2KHBhc3RlMChrdDkyX2RhdGFfZGlyLCAia3Q5Mi5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQojIC0gcGVyY2VudHMgdG8gcHJvYmFiaWxpdHkgaW4gbWVxX2RhdGEKbWVxX2RhdGEkcDEgPC0gbWVxX2RhdGEkcDEvMTAwCm1lcV9kYXRhJHAyIDwtIG1lcV9kYXRhJHAyLzEwMApwcmludChtZXFfZGF0YSkKYGBgCgoKIyMgRXN0aW1hdGluZyB0aGUgRXhwZWN0ZWQgVXRpbGl0eSBUaGVvcnkgKEVVUikgbW9kZWwgdmlhIExTCgpTdGFydCBzaW1wbGU6IGxldCdzIGVzdGltYXRlIHRoZSBFVVQgcGFyYW1ldGVyICh0aGVyZSBpcyBvbmx5IG9uZTogdGhlIHBvd2VyLXV0aWxpdHkgJFxyaG8kIGV4cG9uZW50KSB2aWEgTGVhc3QgU3F1YXJlcy4KCiMjIyBVdGlsaXR5IGZ1bmN0aW9uCgpgdXRpbGl0eV9wb3dlcigpYCBpcyBvdXIgRVVUIHV0aWxpdHkgZnVuY3Rpb24uCgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIHBvd2VyIHV0aWxpdHkgZnVuY3Rpb24KdXRpbGl0eV9wb3dlciA8LSBmdW5jdGlvbih4LCByaG8pIHsKICB1IDwtIHhecmhvCiAgcmV0dXJuKHUpCn0KYGBgCgojIyMgUHJlZGljdGlvbgoKQSBmdW5jdGlvbiBgZXV0X3ByZWRpY3QoKWAgdG8gcHJlZGljdCBtb25ldGFyeSBlcXVpdmFsZW50cyBmcm9tIGEgZGF0YXNldCB2aWEgKipFVVQqKjoKCmBgYHtyIGVjaG8gPSBUUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gZnVuY3Rpb246IEVVVCBwcmVkaWN0aW9ucwpldXRfcHJlZGljdCA8LSBmdW5jdGlvbihkYXRhLCByaG8pIHsKICAKICAjIC0gdXRpbGl0eSBmdW5jdGlvbnMKICB1MSA8LSBpZmVsc2UoZGF0YSR2MSA+PSAwLAogICAgICAgICAgICAgICB1dGlsaXR5X3Bvd2VyKGRhdGEkdjEsIHJobyksCiAgICAgICAgICAgICAgIC11dGlsaXR5X3Bvd2VyKC1kYXRhJHYxLCByaG8pCiAgICAgICAgICAgICAgICkKICB1MiA8LSBpZmVsc2UoZGF0YSR2MiA+PSAwLAogICAgICAgICAgICAgICB1dGlsaXR5X3Bvd2VyKGRhdGEkdjIsIHJobyksCiAgICAgICAgICAgICAgIC11dGlsaXR5X3Bvd2VyKC1kYXRhJHYyLCByaG8pCiAgICAgICAgICAgICAgICkKICAKICAjIC0gcHJlZGljdGlvbnM6IHV0aWxpdHkgc2NhbGUKICBwcmVkaWN0aW9ucyA8LSBkYXRhJHAxKnUxICsgZGF0YSRwMip1MgogIAogICMgLSBwcmVkaWN0aW9uczogdmFsdWUgc2NhbGUKICBwcmVkaWN0aW9ucyA8LSBpZmVsc2UocHJlZGljdGlvbnMgPj0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdGlvbnNeKDEvcmhvKSwKICAgICAgICAgICAgICAgICAgICAgICAgLSgoLXByZWRpY3Rpb25zKV4oMS9yaG8pKSkKICAKICAjIC0gb3V0cHV0CiAgcmV0dXJuKHByZWRpY3Rpb25zKQogIAp9CmBgYAoKKipOT1RFLioqIFRoZSBgcHJlZGljdGlvbnM6IHZhbHVlIHNjYWxlYCBwYXJ0IG9mIHRoZSBjb2RlIGlzIHZlcnkgaW1wb3J0YW50LCBiZWNhdXNlIHdlIGRvIG5vdCB3aXNoIHRvIG1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHV0aWxpdHkgc2NhbGUgYnV0IHJhdGhlciBvbiB0aGUgdmFsdWUgKE1FcSkgc2NhbGUuIFNvLCBsZXQncyBub3QgZm9yZ2V0IHRoYXQKCiQkTUVxID0gdSh4KV57MS9ccmhvfSQkCmZvciBnYWlucywgYW5kCgokJE1FcSA9IC0odSgteCleezEvXHJob30pJCQKZm9yIGxvc3Nlcy4KCkFuZCB0ZXN0IG91ciBwcmVkaWN0aW9ucyBmb3IgJFxyaG89Ljc2JDoKCmBgYHtyIGVjaG8gPSBUUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwcmVkcyA8LSBldXRfcHJlZGljdChkYXRhID0gbWVxX2RhdGEsIHJobyA9IC43NikKcHJpbnQocHJlZHMpCmBgYAoKIyMjIFNTRTogb2JqZWN0aXZlIAoKTm93IHRoZSBvYmplY3RpdmUgZnVuY3Rpb24gYGV1dF9zc2UoKWA6IGl0IGNvbXB1dGVzIHRoZSAkU1NFJCBmb2xsb3dpbmcgdGhlIHByZWRpY3RpdmUgcGFzcyBvZiBgZXV0X3ByZWRpY3QoKWAKCmBgYHtyIGVjaG8gPSBUUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gb3B0aW1pemUgdGhlIEVVVCBtb2RlbCB2aWEgTFMKZXV0X3NzZSA8LSBmdW5jdGlvbihwYXJhbXMpIHsKICByaG8gPC0gYWJzKHBhcmFtc1sxXSkKICBwcmVkcyA8LSBldXRfcHJlZGljdChtZXFfZGF0YSwgcmhvKQogIHNzZSA9IHN1bSgobWVxX2RhdGEkbWVxIC0gcHJlZHMpXjIpCiAgcmV0dXJuKHNzZSkKfQpgYGAKClRlc3QgZm9yICRccmhvPS43NiQ6CgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZXV0X3NzZShwYXJhbXM9Ljc2KQpgYGAKCiMjIyBFVVQgbW9kZWwgb3B0aW1pemF0aW9uIAoKQW5kIHdlIHdhbnQgdG8gbWluaW1pemUgYGV1dF9zc2UoKWAgZm9yIHRoZSBNRXEgZGF0YXNldCBhdCBoYW5kLiBXZSB3aWxsIHVzZSBOZWxkZXItTWVhZCB3aXRoIFIgYG9wdGltKClgOgoKYGBge3IgZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSByYW5kb20gaW5pdGlhbCB2YWx1ZQppbml0X3JobyA8LSBydW5pZigxLCAwLCAxKQpzb2x1dGlvbiA8LSBvcHRpbShwYXIgPSBpbml0X3JobywgCiAgICAgICAgICAgICAgICAgIGZuID0gZXV0X3NzZSwgCiAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJOZWxkZXItTWVhZCIsIAogICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdCgibWF4aXQiID0gMTBlNikpCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIFJobyBvZiAiLCByb3VuZChzb2x1dGlvbiRwYXIsIDIpKSkKcHJpbnQocGFzdGUwKCJDb252ZXJnZW5jZSBjaGVjazogIiwgc29sdXRpb24kY29udmVyZ2VuY2UpKQpgYGAKCk5vdyBsZXQncyBwcmVkaWN0IHRoZSBvYnNlcnZlZCBNRXFzIGZyb20gdGhlIGVzdGltYXRlZCBwYXJhbWV0ZXI6CgpgYGB7ciBlY2hvID0gVFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIHByZWRpY3QgZnJvbSB0aGUgb3B0aW1pemVkIEVVVCBtb2RlbAptZXFfZGF0YSRldXRfbWVxIDwtIGV1dF9wcmVkaWN0KG1lcV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJobyA9IHNvbHV0aW9uJHBhcikKcHJpbnQobWVxX2RhdGEpCmBgYAoKQW5kIHBsb3QgdGhlIHByZWRpY3Rpb25zOgoKYGBge3IgZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSBwbG90IHByZWRpY3Rpb25zCmdncGxvdChkYXRhID0gbWVxX2RhdGEsCiAgICAgICBhZXMoeCA9IGV1dF9tZXEsIHkgPSBtZXEpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNpemUgPSAuMjUsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJibGFjayIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUocGFzdGUwKCJFVVQsIEVzdGltYXRlZCBSaG8gb2YgIiwgcm91bmQoc29sdXRpb24kcGFyLCAyKSkpICsKICB4bGFiKCJQcmVkaWN0ZWQgTUVxIikgKyAKICB5bGFiKCJPYnNlcnZlZCBNRXEiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKYGBgCgpMZXQncyBydW4gMTAwIG9wdGltaXphdGlvbnMgdG8gY2hlY2sgaWYgd2UgYXJlIHN0dWNrIGluIHNvbWUgbG9jYWwgbWluaW11bSBvbmx5OgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gbWFueSBvcHRpbWl6YXRpb24gcnVucwppbml0X3JobyA8LSBydW5pZigxMDAsIDAsIDEpCgojIC0gcnVuCnJlc3VsdHMgPC0gbGFwcGx5KGluaXRfcmhvLCBmdW5jdGlvbih4KSB7CiAgCiAgIyAtIG9wdGltaXphdGlvbiBydW4KICBvcHRpbV9zb2wgPC0gdHJ5Q2F0Y2gob3B0aW0ocGFyID0geCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm4gPSBldXRfc3NlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTmVsZGVyLU1lYWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdCgibWF4aXQiID0gMTBlNikpLAogICAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybihOVUxMKQogICAgICAgICAgICAgICAgICAgICAgICB9KQogIAogIGlmICghaXMubnVsbChvcHRpbV9zb2wpKSB7CiAgICAKICAgICMgLSBwcmVkaWN0aW9ucwogICAgcHJlZHMgPC0gZXV0X3ByZWRpY3QobWVxX2RhdGEsIHJobyA9IG9wdGltX3NvbCRwYXIpCiAgICAKICAgIHJldHVybigKICAgICAgbGlzdChyaG8gPSBvcHRpbV9zb2wkcGFyLAogICAgICAgICAgIHNzZSA9IG9wdGltX3NvbCR2YWx1ZSwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IG9wdGltX3NvbCRjb252ZXJnZW5jZSwKICAgICAgICAgICBwcmVkaWN0aW9ucyA9IHByZWRzKQogICAgICApCiAgICAKICB9IGVsc2UgewogICAgICAKICAgICAgcmV0dXJuKAogICAgICBsaXN0KHJobyA9IE5VTEwsCiAgICAgICAgICAgc3NlID0gTlVMTCwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IE5VTEwsCiAgICAgICAgICAgcHJlZGljdGlvbnMgPSBOVUxMKQogICAgICApCiAgICAKICB9CiAgCiAgCn0pCgojIC0gZmluZCBiZXN0IHNvbHV0aW9uCnNzZV9saXN0IDwtIHNhcHBseShyZXN1bHRzLCBmdW5jdGlvbih4KSB4JHNzZSkKYmVzdF9ydW4gPC0gd2hpY2gubWluKHNzZV9saXN0KVsxXQpwcmludChwYXN0ZTAoIkNoZWNrIGNvbnZlcmdlbmNlOiAiLCByZXN1bHRzW1tiZXN0X3J1bl1dJGNvbnZlcmdlbmNlKSkKZXN0aW1hdGVkX3JobyA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJHJobwpvcHRpbWFsX3ByZWRpY3Rpb25zIDwtIHJlc3VsdHNbW2Jlc3RfcnVuXV0kcHJlZGljdGlvbnMKCiMgLSBwbG90IHByZWRpY3Rpb25zCnBsb3RfZnJhbWUgPC0gbWVxX2RhdGEKbWVxX2RhdGEkZXV0X21lcSA8LSBvcHRpbWFsX3ByZWRpY3Rpb25zCmdncGxvdChkYXRhID0gbWVxX2RhdGEsCiAgICAgICBhZXMoeCA9IG1lcSwgeSA9IGV1dF9tZXEpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNpemUgPSAuMjUsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJibGFjayIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUocGFzdGUwKCJFVVQsIEVzdGltYXRlZCBSaG8gb2YgIiwgcm91bmQoZXN0aW1hdGVkX3JobywgMikpKSArCiAgeGxhYigiUHJlZGljdGVkIE1FcSIpICsgCiAgeWxhYigiT2JzZXJ2ZWQgTUVxIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKIyMjIEVVVCBtb2RlbCBlcnJvciBzdXJmYWNlCgpUaGUgRVVUIG1vZGVsIGVycm9yIHN1cmZhY2UgZnJvbSBhIHNhbXBsZSBvZiAxMDAsMDAwIHJhbmRvbSBwYXJhbWV0ZXIgdmFsdWVzOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gc2FtcGxlIHBhcmFtZXRlcnMgZnJvbSBhIHJhbmdlIGZvciBwbG90dGluZyBwdXJwb3NlcwpzYW1wbGVfcGFyYW1ldGVycyA8LSBkYXRhLmZyYW1lKHJobyA9IHJ1bmlmKDEwMDAwMCwgMCwgMSkpCnNhbXBsZV9wYXJhbWV0ZXJzJHNzZSA8LSBzYXBwbHkoc2FtcGxlX3BhcmFtZXRlcnMkcmhvLCBldXRfc3NlKQpoZWFkKHNhbXBsZV9wYXJhbWV0ZXJzKQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ2dwbG90KGRhdGEgPSBzYW1wbGVfcGFyYW1ldGVycywKICAgICAgIGFlcyh4ID0gcmhvLCB5ID0gc3NlKSkgKyAKICBnZW9tX2xpbmUoc2l6ZSA9IC4yNSwgY29sb3IgPSAiYmxhY2siLCBncm91cD0xKSArIAogIGdndGl0bGUoIkVVVCBNb2RlbCBFcnJvciBTdXJmYWNlICIpICsKICB4bGFiKCJSaG8iKSArIAogIHlsYWIoIlNTRSIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0PS41LCBzaXplID0gMTApKQpgYGAKCiMjIEVzdGltYXRpbmcgdGhlIEV4cGVjdGVkIFV0aWxpdHkgVGhlb3J5IChFVVIpIG1vZGVsIHZpYSBNTEUKCiMjIyBBcHByb2FjaCB0byBNTEUgZm9yIEVVVAoKRm9yIHRoZSBNYXhpbXVtLUxpa2VsaWhvb2QgRXN0aW1hdGlvbiAoTUxFKSBvZiBFVVQsIGxldCdzIGFzc3VtZSB0aGUgZm9sbG93aW5nOgoKLSB0aGUgZGVjaXNpb24gbWFrZXIgY29tcHV0ZXMgdGhlICR1KHgpJCBpbnRlcm5hbGx5LCBhbmQgdGhlCi0gcmVzcG9uZHMgd2l0aCAkdSh4KStOb3JtYWwoXG11PTAsXHNpZ21hKSQsIGkuZS4gYWRkaW5nIGEgcmFuZG9tIG5vaXNlIHRvIHRoZWlyIHJlc3BvbnNlLgoKVGhlbiB3ZSBjYW4gb2J0YWluIHRoZSBsaWtlbGlob29kIG9mIGVhY2ggTUVxIG9ic2VydmF0aW9uIGZyb20gJE5vcm1hbChyZXNwb25zZS1wcmVkaWN0aW9uLCBcc2lnbWEpJCwgZXN0aW1hdGluZyBvbmUgYWRkaXRpb25hbCByZXNwb25zZSBtb2RlbCBwYXJhbWV0ZXIgJFxzaWdtYSQuCgpUaGUgZnVuY3Rpb24gYGV1dF9saWtlbGlob29kYCByZXR1cm5zIHRoZSBuZWdhdGl2ZSBsb2ctbGlrZWxpaG9vZCBmb3IgYSBkYXRhc2V0IGdpdmVuIHBhcmFtZXRlcnMgJFxyaG8sXHNpZ21hJDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIGZ1bmN0aW9uOiBFVVQgbW9kZWwgbGlrZWxpaG9vZApldXRfbGlrZWxpaG9vZCA8LSBmdW5jdGlvbihkYXRhLCByaG8sIHNpZ21hKSB7CiAgCiAgIyAtIHV0aWxpdHkgZnVuY3Rpb25zCiAgdTEgPC0gaWZlbHNlKGRhdGEkdjEgPj0gMCwKICAgICAgICAgICAgICAgdXRpbGl0eV9wb3dlcihkYXRhJHYxLCByaG8pLAogICAgICAgICAgICAgICAtdXRpbGl0eV9wb3dlcigtZGF0YSR2MSwgcmhvKQogICAgICAgICAgICAgICApCiAgdTIgPC0gaWZlbHNlKGRhdGEkdjIgPj0gMCwKICAgICAgICAgICAgICAgdXRpbGl0eV9wb3dlcihkYXRhJHYyLCByaG8pLAogICAgICAgICAgICAgICAtdXRpbGl0eV9wb3dlcigtZGF0YSR2MiwgcmhvKQogICAgICAgICAgICAgICApCiAgCiAgIyAtIHByZWRpY3Rpb25zOiB1dGlsaXR5IHNjYWxlCiAgcHJlZGljdGlvbnMgPC0gZGF0YSRwMSp1MSArIGRhdGEkcDIqdTIKICAKICAjIC0gcHJlZGljdGlvbnM6IHZhbHVlIHNjYWxlCiAgcHJlZGljdGlvbnMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID49IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb25zXigxL3JobyksCiAgICAgICAgICAgICAgICAgICAgICAgIC0oKC1wcmVkaWN0aW9ucyleKDEvcmhvKSkpCiAgCiAgIyAtIGxpa2VsaWhvb2QKICAjIC0gYXNzdW1lIHRoYXQgdGhlIGRlY2lzaW9uIG1ha2VyIGNvbXB1dGVzIAogICMgLSB0aGUgdXRpbGl0eSBvZiB0aGUgZ2FtYmxlIGFuZCBpdHMgTUVRLCBhbmQgdGhlbgogICMgLSByZXNwb25kcyB3aXRoIGFuIGFkZGVkIHJhbmRvbSBub2lzZSBvZiBOb3JtYWwoMCwgc2lnbWEpCiAgbmVnX2xvZ2xpa2UgPC0gLXN1bSgKICAgIGRub3JtKHByZWRpY3Rpb25zIC0gZGF0YSRtZXEsIG1lYW4gPSAwLCBzZCA9IHNpZ21hLCBsb2cgPSBUUlVFKQogICkKCiAgIyAtIG91dHB1dAogIHJldHVybihuZWdfbG9nbGlrZSkKICAKfQpgYGAKClRlc3QgYGV1dF9saWtlbGlob29kYCBmb3IgJFxyaG89Ljc2JCBhbmQgJFxzaWdtYT0xMSQ6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5sbCA8LSBldXRfbGlrZWxpaG9vZChkYXRhID0gbWVxX2RhdGEsIHJobyA9IC43Niwgc2lnbWEgPSAxMSkKcHJpbnQobmxsKQpgYGAKCiMjIyBUaGUgb2JqZWN0aXZlCgpJbnRyb2R1Y2UgdGhlIG9iamVjdGl2ZSBgZXV0X21sZSgpYCBmdW5jdGlvbgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gb3B0aW1pemUgdGhlIEVVVCBtb2RlbCB2aWEgTFMKZXV0X21sZSA8LSBmdW5jdGlvbihwYXJhbXMpIHsKICByaG8gPC0gYWJzKHBhcmFtc1sxXSkKICBzaWdtYSA8LSBhYnMocGFyYW1zWzJdKQogIG5sbCA8LSBldXRfbGlrZWxpaG9vZChkYXRhID0gbWVxX2RhdGEsIHJobywgc2lnbWEpCiAgcmV0dXJuKG5sbCkKfQpgYGAKCiMjIyBPcHRpbWl6ZSBFVVQgbW9kZWwKCkFuZCBvcHRpbWl6ZSBgZXV0X21sZSgpYCBmb3IgYG1lcV9kYXRhYDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIHJhbmRvbSBpbml0aWFsIHZhbHVlCmluaXRfcmhvIDwtIHJ1bmlmKDEsIDAsIDEpCmluaXRfc2lnbWEgPC0gcnVuaWYoMSwgMCwgMSkKc29sdXRpb24gPC0gb3B0aW0ocGFyID0gYyhpbml0X3JobywgaW5pdF9zaWdtYSksIAogICAgICAgICAgICAgICAgICBmbiA9IGV1dF9tbGUsIAogICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTmVsZGVyLU1lYWQiLCAKICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3QoIm1heGl0IiA9IDEwZTYpKQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gb2YgIiwgcm91bmQoYWJzKHNvbHV0aW9uJHBhclsxXSksIDIpKSkKcHJpbnQocGFzdGUwKCJFc3RpbWF0ZWQgU2lnbWEgb2YgIiwgcm91bmQoYWJzKHNvbHV0aW9uJHBhclsyXSksIDIpKSkKcHJpbnQocGFzdGUwKCJDb252ZXJnZW5jZSBjaGVjazogIiwgc29sdXRpb24kY29udmVyZ2VuY2UpKQoKIyAtIHByZWRpY3QgZnJvbSB0aGUgb3B0aW1pemVkIEVVVCBtb2RlbAptZXFfZGF0YSRldXRfbWVxIDwtIGV1dF9wcmVkaWN0KG1lcV9kYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJobyA9IGFicyhzb2x1dGlvbiRwYXJbMV0pKQoKIyAtIHBsb3QgcHJlZGljdGlvbnMKZ2dwbG90KGRhdGEgPSBtZXFfZGF0YSwKICAgICAgIGFlcyh4ID0gbWVxLCB5ID0gZXV0X21lcSkpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2l6ZSA9IC4yNSwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImJsYWNrIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZShwYXN0ZTAoIkVVVCwgRXN0aW1hdGVkIFJobyBvZiAiLCByb3VuZChhYnMoc29sdXRpb24kcGFyKSwgMikpKSArCiAgeGxhYigiUHJlZGljdGVkIE1FcSIpICsgCiAgeWxhYigiT2JzZXJ2ZWQgTUVxIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKQ2hlY2sgYnkgMTAwIG9wdGltaXphdGlvbiBydW5zOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gbWFueSBvcHRpbWl6YXRpb24gcnVucwppbml0X3BhcnMgPC0gbGFwcGx5KDE6MTAwLCBmdW5jdGlvbih4KSB7CiAgcmV0dXJuKAogICAgbGlzdCgKICAgICAgaW5pdF9yaG8gPSBydW5pZigxLCAwLCAxKSwKICAgICAgaW5pdF9zaWdtYSA9IHJ1bmlmKDEsIDAsIDEpCiAgICApCiAgKQp9KQoKIyAtIHJ1bgpyZXN1bHRzIDwtIGxhcHBseShpbml0X3BhcnMsIGZ1bmN0aW9uKHgpIHsKICAKICBpbml0X3JobyA8LSB4JGluaXRfcmhvCiAgaW5pdF9zaWdtYSA8LSB4JGluaXRfc2lnbWEKICAKICAjIC0gb3B0aW1pemF0aW9uIHJ1bgogIG9wdGltX3NvbCA8LSB0cnlDYXRjaChvcHRpbShwYXIgPSBjKGluaXRfcmhvLCBpbml0X3NpZ21hKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm4gPSBldXRfbWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTmVsZGVyLU1lYWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdCgibWF4aXQiID0gMTBlNikpLAogICAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybihOVUxMKQogICAgICAgICAgICAgICAgICAgICAgICB9KQogIAogIGlmICghaXMubnVsbChvcHRpbV9zb2wpKSB7CiAgICAKICAgICMgLSBwcmVkaWN0aW9ucwogICAgcHJlZHMgPC0gZXV0X3ByZWRpY3QobWVxX2RhdGEsIHJobyA9IGFicyhvcHRpbV9zb2wkcGFyWzFdKSkKCiAgICByZXR1cm4oCiAgICAgIGxpc3QocmhvID0gb3B0aW1fc29sJHBhclsxXSwKICAgICAgICAgICBzaWdtYSA9IG9wdGltX3NvbCRwYXJbMl0sCiAgICAgICAgICAgbmxsID0gb3B0aW1fc29sJHZhbHVlLAogICAgICAgICAgIGNvbnZlcmdlbmNlID0gb3B0aW1fc29sJGNvbnZlcmdlbmNlLAogICAgICAgICAgIHByZWRpY3Rpb25zID0gcHJlZHMpCiAgICAgICkKICAgIAogIH0gZWxzZSB7CiAgICAgIAogICAgICByZXR1cm4oCiAgICAgIGxpc3QocmhvID0gTlVMTCwKICAgICAgICAgICBzaWdtYSA9IE5VTEwsCiAgICAgICAgICAgbmxsID0gTlVMTCwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IE5VTEwsCiAgICAgICAgICAgcHJlZGljdGlvbnMgPSBOVUxMKQogICAgICApCiAgICAKICB9CiAgCiAgCn0pCgojIC0gZmluZCBiZXN0IHNvbHV0aW9uCm5sbF9saXN0IDwtIHNhcHBseShyZXN1bHRzLCBmdW5jdGlvbih4KSB4JG5sbCkKYmVzdF9ydW4gPC0gd2hpY2gubWluKG5sbF9saXN0KVsxXQpwcmludChwYXN0ZTAoIkNoZWNrIGNvbnZlcmdlbmNlOiAiLCByZXN1bHRzW1tiZXN0X3J1bl1dJGNvbnZlcmdlbmNlKSkKZXN0aW1hdGVkX3JobyA8LSBhYnMocmVzdWx0c1tbYmVzdF9ydW5dXSRyaG8pCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIFJobzogIiwgZXN0aW1hdGVkX3JobykpCmVzdGltYXRlZF9zaWdtYSA8LSBhYnMocmVzdWx0c1tbYmVzdF9ydW5dXSRzaWdtYSkKcHJpbnQocGFzdGUwKCJFc3RpbWF0ZWQgU2lnbWE6ICIsIGVzdGltYXRlZF9zaWdtYSkpCm9wdGltYWxfcHJlZGljdGlvbnMgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRwcmVkaWN0aW9ucwoKIyAtIHBsb3QgcHJlZGljdGlvbnMKcGxvdF9mcmFtZSA8LSBtZXFfZGF0YQptZXFfZGF0YSRldXRfbWVxIDwtIG9wdGltYWxfcHJlZGljdGlvbnMKZ2dwbG90KGRhdGEgPSBtZXFfZGF0YSwKICAgICAgIGFlcyh4ID0gbWVxLCB5ID0gZXV0X21lcSkpICsgCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2l6ZSA9IC4yNSwgY29sb3IgPSAicmVkIiwgc2UgPSBGQUxTRSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0gImJsYWNrIikgKyAKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgZ2d0aXRsZShwYXN0ZTAoIkVVVCwgRXN0aW1hdGVkIFJobyBvZiAiLCByb3VuZChlc3RpbWF0ZWRfcmhvLCAyKSkpICsKICB4bGFiKCJQcmVkaWN0ZWQgTUVxIikgKyAKICB5bGFiKCJPYnNlcnZlZCBNRXEiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKCmBgYAoKIyMjIFRoZSBFVVQgTmVnYXRpdmUgTG9nLUxpa2VsaWhvb2Qgc3VyZmFjZQoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gc2FtcGxlIHBhcmFtZXRlcnMgZnJvbSBhIHJhbmdlIGZvciBwbG90dGluZyBwdXJwb3NlcwpzYW1wbGVfcGFyYW1ldGVycyA8LSBkYXRhLmZyYW1lKHJobyA9IHJ1bmlmKDEwMDAwMCwgMCwgMS41KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnbWEgPSBydW5pZigxMDAwMDAsIDAsIDE1KSkKc2FtcGxlX3BhcmFtZXRlcnMkbmxsIDwtIGFwcGx5KHNhbXBsZV9wYXJhbWV0ZXJzLCAxLCBmdW5jdGlvbih4KSB7CiAgICBldXRfbWxlKHBhcmFtcyA9IGMoeFsxXSwgeFsyXSkpCn0pCmhlYWQoc2FtcGxlX3BhcmFtZXRlcnMpCmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwbG90X2x5KCkgJT4lIAogIGFkZF90cmFjZShkYXRhID0gc2FtcGxlX3BhcmFtZXRlcnMsICAKICAgICAgICAgICAgeCA9IHNhbXBsZV9wYXJhbWV0ZXJzJHJobywgCiAgICAgICAgICAgIHkgPSBzYW1wbGVfcGFyYW1ldGVycyRzaWdtYSwgCiAgICAgICAgICAgIHogPSBzYW1wbGVfcGFyYW1ldGVycyRubGwsIAogICAgICAgICAgICB0eXBlID0gIm1lc2gzZCIsIAogICAgICAgICAgICBpbnRlbnNpdHkgPSBzYW1wbGVfcGFyYW1ldGVycyRubGwpICU+JSAKICBsYXlvdXQoCiAgICBtb2RlYmFyID0gbGlzdChvcmllbnRhdGlvbiA9ICJ2IiksIAogICAgdGl0bGUgPSAiVGhlIEVVVCBOZWdhdGl2ZSBMb2ctTGlrZWhvb2QgZnVuY3Rpb24iLAogICAgc2NlbmUgPSBsaXN0KAogICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiUmhvIiwgcmFuZ2UgPSBjKDAsIDEuNSkpLAogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU2lnbWEiLCByYW5nZSA9IGMoMCwgMTUpKSwKICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gIk5lZy1Mb2dMaWtlIiwgcmFuZ2UgPSBjKDAsIDEwMDApKQogICAgKSkgJT4lIGhpZGVfY29sb3JiYXIoKQpgYGAKCiMjIEVzdGltYXRpbmcgdGhlIChDdW11bGF0aXZlKSBQcm9zcGVjdCBUaGVvcnkgKENQVCkgbW9kZWwgdmlhIE1MRQoKV2Ugd2lsbCBiZWdpbiB3aXRoIGEgZGlmZmVyZW50LCBsYXJnZXIgZGF0YSBzZXQgKE1pbG92YW5vdmljLCAyMDEzKToKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIGxvYWQgc29tZSBNRXEgZXhwZXJpbWVudGFsIGRhdGEKbWVxX2RhdGEgPC0gcmVhZC5jc3YocGFzdGUwKG1lcV9kYXRhX2RpciwgImVzNHYxLmNzdiIpLAogICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxLAogICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiMgLSBwZXJjZW50cyB0byBwcm9iYWJpbGl0eSBpbiBtZXFfZGF0YQptZXFfZGF0YSRwMSA8LSBtZXFfZGF0YSRwMS8xMDAKbWVxX2RhdGEkcDIgPC0gbWVxX2RhdGEkcDIvMTAwCnByaW50KG1lcV9kYXRhKQpgYGAKCiMjIyBUaGUgUFQgUmVmZXJlbmNlLURlcGVuZGVudCBQb3dlciBVdGlsaXR5IEZ1bmN0aW9uCgpFbnRlcnMgYHJkX3V0aWxpdHlfcG93ZXIoKWA6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSByZWZlcmVuY2VfZGVwZW5kZW50IHBvd2VyIHV0aWxpdHkgZnVuY3Rpb24KcmRfdXRpbGl0eV9wb3dlciA8LSBmdW5jdGlvbih4LCByaG9fZywgcmhvX2wsIGxhbWJkYSkgewogIHUgPC0gaWZlbHNlKHggPj0gMCwgCiAgICAgICAgICAgICAgeF5yaG9fZywKICAgICAgICAgICAgICAtbGFtYmRhKigoLXgpXnJob19sKSkKICByZXR1cm4odSkKfQpgYGAKCiMjIyBQcm9iYWJpbGl0eSBXZWlnaHRpbmcgRnVuY3Rpb24KCkVudGVycyBgcF93ZWlnaHQoKWAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIFByZWxlYydzIHNpbmdsZS1wYXJhbWV0ZXIgcHJvYmFiaWxpdHkgd2VpZ2h0aW5nIGZ1bmN0aW9uCiMgLSBOT1RFOiB3KHApIHdpbGwgYmUgYXBwbGllZCBvdmVyIGdhaW5zIGFuZCBsb3NzZXMgc2VwYXJhdGVseSAKcF93ZWlnaHQgPC0gZnVuY3Rpb24ocCwgZ2FtbWEpIHsKICAKICB3cCA8LSBleHAoLSgtbG9nKHApKV5nYW1tYSkKICByZXR1cm4od3ApCgp9CmBgYAoKIyMjIFRoZSBQcm9zcGVjdCBUaGVvcnkgTmVnYXRpdmUgTG9nLUxpa2VsaWhvb2QKCiMjIyBQVCBvcHRpbWl6YXRpb24gYW5kIHByZWRpY3Rpb25zCgpgY3B0X2xpa2VsaWhvb2QoKWAgcmV0dXJucyB0aGUgbmVnYXRpdmUgbG9nLWxpa2VsaWhvb2QgZm9yIGEgZ2l2ZW4gZGF0YXNldDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIGZ1bmN0aW9uOiBDUFQgbW9kZWwgbGlrZWxpaG9vZApjcHRfbGlrZWxpaG9vZCA8LSBmdW5jdGlvbihkYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmhvX2csIAogICAgICAgICAgICAgICAgICAgICAgICAgICByaG9fbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2dhaW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYV9sb3NzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdtYSkgewogIAogICMgLSB1dGlsaXR5IGZ1bmN0aW9ucwogIHUxIDwtIHJkX3V0aWxpdHlfcG93ZXIoZGF0YSR2MSwgcmhvX2csIHJob19sLCBsYW1iZGEpCiAgdTIgPC0gcmRfdXRpbGl0eV9wb3dlcihkYXRhJHYyLCByaG9fZywgcmhvX2wsIGxhbWJkYSkKICAKICAjIC0gcHJvYmFiaWxpdHkgd2VpZ2h0aW5nCiAgd3AxIDwtIHZlY3Rvcihtb2RlID0gIm51bWVyaWMiLCBsZW5ndGggPSBsZW5ndGgoZGF0YSR2MSkpCiAgaXhfZ2Fpbl8xIDwtIHdoaWNoKGRhdGEkdjEgPj0gMCkKICB3cDFbaXhfZ2Fpbl8xXSA8LSBwX3dlaWdodChkYXRhJHAxW2l4X2dhaW5fMV0sIGdhbW1hX2dhaW4pCiAgaXhfbG9zc18xIDwtIHdoaWNoKGRhdGEkdjEgPCAwKQogIHdwMVtpeF9sb3NzXzFdIDwtIHBfd2VpZ2h0KGRhdGEkcDFbaXhfbG9zc18xXSwgZ2FtbWFfbG9zcykKICB3cDIgPC0gMS13cDEKICAKICAjIC0gcHJlZGljdGlvbnM6IHV0aWxpdHkgc2NhbGUKICBwcmVkaWN0aW9ucyA8LSB3cDEqdTEgKyB3cDIqdTIKICAKICAjIC0gcHJlZGljdGlvbnM6IHZhbHVlIHNjYWxlCiAgcHJlZGljdGlvbnMgPC0gaWZlbHNlKHByZWRpY3Rpb25zID49IDAsCiAgICAgICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb25zXigxL3Job19nKSwKICAgICAgICAgICAgICAgICAgICAgICAgLSgoKC1wcmVkaWN0aW9ucykvbGFtYmRhKV4oMS9yaG9fbCkpCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAKICAjIC0gbGlrZWxpaG9vZAogICMgLSBhc3N1bWUgdGhhdCB0aGUgZGVjaXNpb24gbWFrZXIgY29tcHV0ZXMgCiAgIyAtIHRoZSB1dGlsaXR5IG9mIHRoZSBnYW1ibGUgYW5kIGl0cyBNRVEsIGFuZCB0aGVuCiAgIyAtIHJlc3BvbmRzIHdpdGggYW4gYWRkZWQgcmFuZG9tIG5vaXNlIG9mIE5vcm1hbCgwLCBzaWdtYSkKICBuZWdfbG9nbGlrZSA8LSAtc3VtKAogICAgZG5vcm0ocHJlZGljdGlvbnMgLSBkYXRhJG1lcSwgbWVhbiA9IDAsIHNkID0gc2lnbWEsIGxvZyA9IFRSVUUpCiAgKQoKICAjIC0gb3V0cHV0CiAgcmV0dXJuKG5lZ19sb2dsaWtlKQogIAp9CmBgYAoKVGVzdCBgY3B0X2xpa2VsaWhvb2QoKWAgZm9yIGEgc2V0IG9mIHBhcmFtZXRlcnM6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5sbCA8LSBjcHRfbGlrZWxpaG9vZChkYXRhID0gbWVxX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgcmhvX2cgPSAuNzEsIAogICAgICAgICAgICAgICAgICAgICAgcmhvX2wgPSAuNjksIAogICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gMiwgCiAgICAgICAgICAgICAgICAgICAgICBnYW1tYV9nYWluID0gLjY3LCAKICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2xvc3MgPSAuNjEsIAogICAgICAgICAgICAgICAgICAgICAgc2lnbWEgPSAuMTQpCnByaW50KG5sbCkKYGBgCgpBbmQgZmluYWxseSBtaW5pbWl6ZSBgY3B0X2xpa2VsaWhvb2QoKWAgZm9yIGBtZXFfZGF0YWA6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSBvcHRpbWl6ZSB0aGUgQ1BUIG1vZGVsIHZpYSBMUwpjcHRfbWxlIDwtIGZ1bmN0aW9uKHBhcmFtcykgewogIHJob19nIDwtIGFicyhwYXJhbXNbMV0pCiAgcmhvX2wgPC0gYWJzKHBhcmFtc1syXSkKICBsYW1iZGEgPC0gYWJzKHBhcmFtc1szXSkKICBnYW1tYV9nYWluIDwtIGFicyhwYXJhbXNbNF0pCiAgZ2FtbWFfbG9zcyA8LSBhYnMocGFyYW1zWzVdKQogIHNpZ21hIDwtIGFicyhwYXJhbXNbNl0pCiAgbmxsIDwtIGNwdF9saWtlbGlob29kKGRhdGEgPSBtZXFfZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgIHJob19nLCByaG9fbCwgbGFtYmRhLAogICAgICAgICAgICAgICAgICAgICAgICBnYW1tYV9nYWluLCBnYW1tYV9sb3NzLCBzaWdtYSkKICByZXR1cm4obmxsKQp9CgojIC0gcmFuZG9tIGluaXRpYWwgdmFsdWUKaW5pdF9yaG9fZyA8LSBydW5pZigxLCAwLCAxKQppbml0X3Job19sIDwtIHJ1bmlmKDEsIDAsIDEpCmluaXRfbGFtYmRhIDwtIHJ1bmlmKDEsIDAsIDEwKQppbml0X2dhbW1hX2dhaW4gPC0gcnVuaWYoMSwgMCwgMTApCmluaXRfZ2FtbWFfbG9zcyA8LSBydW5pZigxLCAwLCAxKQppbml0X3NpZ21hIDwtIHJ1bmlmKDEsIDAsIDEpCnNvbHV0aW9uIDwtIG9wdGltKHBhciA9IGMoaW5pdF9yaG9fZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdF9yaG9fbCwKICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0X2xhbWJkYSwKICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0X2dhbW1hX2dhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdF9nYW1tYV9sb3NzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRfc2lnbWEpLCAKICAgICAgICAgICAgICAgICAgZm4gPSBjcHRfbWxlLCAKICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIk5lbGRlci1NZWFkIiwgCiAgICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBsaXN0KCJtYXhpdCIgPSAxMGU2KSkKcHJpbnQocGFzdGUwKCJFc3RpbWF0ZWQgUmhvIGZvciBnYWlucyBvZiAiLCByb3VuZChhYnMoc29sdXRpb24kcGFyWzFdKSwgMikpKQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gZm9yIGxvc3NlcyBvZiAiLCByb3VuZChhYnMoc29sdXRpb24kcGFyWzJdKSwgMikpKQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBMYW1iZGEgKGxvc3MgYXZlcnNpb24pIG9mICIsIHJvdW5kKGFicyhzb2x1dGlvbiRwYXJbM10pLCAyKSkpCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIEdhbW1hIGZvciBnYWlucyBvZiAiLCByb3VuZChhYnMoc29sdXRpb24kcGFyWzRdKSwgMikpKQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBHYW1tYSBmb3IgbG9zc2VzIG9mICIsIHJvdW5kKGFicyhzb2x1dGlvbiRwYXJbNV0pLCAyKSkpCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIFNpZ21hIG9mICIsIHJvdW5kKGFicyhzb2x1dGlvbiRwYXJbNl0pLCAyKSkpCnByaW50KHBhc3RlMCgiQ29udmVyZ2VuY2UgY2hlY2s6ICIsIHNvbHV0aW9uJGNvbnZlcmdlbmNlKSkKYGBgCgpgY3B0X3ByZWRpY3QoKWAgcHJlZGljdHMgTW9uZXRhcnkgRXF1aXZhbGVudHMgZm9yIGEgZ2l2ZW4gZGF0YXNldDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIGZ1bmN0aW9uOiBDUFQgbW9kZWwgcHJlZGljdGlvbnMKY3B0X3ByZWRpY3QgPC0gZnVuY3Rpb24oZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgcmhvX2csIHJob19sLCBsYW1iZGEsCiAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2dhaW4sIGdhbW1hX2xvc3MpIHsKICAKICAjIC0gdXRpbGl0eSBmdW5jdGlvbnMKICB1MSA8LSByZF91dGlsaXR5X3Bvd2VyKGRhdGEkdjEsIHJob19nLCByaG9fbCwgbGFtYmRhKQogIHUyIDwtIHJkX3V0aWxpdHlfcG93ZXIoZGF0YSR2MiwgcmhvX2csIHJob19sLCBsYW1iZGEpCiAgCiAgIyAtIHByb2JhYmlsaXR5IHdlaWdodGluZwogIHdwMSA8LSB2ZWN0b3IobW9kZSA9ICJudW1lcmljIiwgbGVuZ3RoID0gbGVuZ3RoKGRhdGEkdjEpKQogIGl4X2dhaW5fMSA8LSB3aGljaChkYXRhJHYxID49IDApCiAgd3AxW2l4X2dhaW5fMV0gPC0gcF93ZWlnaHQoZGF0YSRwMVtpeF9nYWluXzFdLCBnYW1tYV9nYWluKQogIGl4X2xvc3NfMSA8LSB3aGljaChkYXRhJHYxIDwgMCkKICB3cDFbaXhfbG9zc18xXSA8LSBwX3dlaWdodChkYXRhJHAxW2l4X2xvc3NfMV0sIGdhbW1hX2xvc3MpCiAgd3AyIDwtIDEtd3AxCiAgCiAgIyAtIHByZWRpY3Rpb25zOiB1dGlsaXR5IHNjYWxlCiAgcHJlZGljdGlvbnMgPC0gd3AxKnUxICsgd3AyKnUyCiAgCiAgIyAtIHByZWRpY3Rpb25zOiB2YWx1ZSBzY2FsZQogIHByZWRpY3Rpb25zIDwtIGlmZWxzZShwcmVkaWN0aW9ucyA+PSAwLAogICAgICAgICAgICAgICAgICAgICAgICBwcmVkaWN0aW9uc14oMS9yaG9fZyksCiAgICAgICAgICAgICAgICAgICAgICAgIC0oKCgtcHJlZGljdGlvbnMpL2xhbWJkYSleKDEvcmhvX2wpKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgCiAgIyAtIG91dHB1dAogIHJldHVybihwcmVkaWN0aW9ucykKICAKfQpgYGAKCioqTk9URS4qKiBUaGUgYHByZWRpY3Rpb25zOiB2YWx1ZSBzY2FsZWAgcGFydCBvZiB0aGUgY29kZSBpcyB2ZXJ5IGltcG9ydGFudCwgYmVjYXVzZSB3ZSBkbyBub3Qgd2lzaCB0byBtYWtlIHByZWRpY3Rpb25zIG9uIHRoZSB1dGlsaXR5IHNjYWxlIGJ1dCByYXRoZXIgb24gdGhlIHZhbHVlIChNRXEpIHNjYWxlLiBTbywgbGV0J3Mgbm90IGZvcmdldCB0aGF0ICoqdW5kZXIgUHJvc3BlY3QgVGhlb3J5Kiogd2UgaGF2ZToKCiQkTUVxID0gdV9nKHgpXnsxL1xyaG9fZ30kJAp3aGlsZSBmb3IgbG9zw59lcyB3ZSBoYXZlCgokJE1FcSA9IC0oKFxmcmFje3VfbCgteCl9e1xsYW1iZGF9KV57MS9ccmhvX2x9KSQkCgpUZXN0IGBjcHRfcHJlZGljdCgpYDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIHByZWRpY3QgZnJvbSB0aGUgb3B0aW1pemVkIENQVCBtb2RlbAptZXFfZGF0YSRjcHRfbWVxIDwtIGNwdF9wcmVkaWN0KGRhdGEgPSBtZXFfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaG9fZyA9IGFicyhzb2x1dGlvbiRwYXJbMV0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJob19sID0gYWJzKHNvbHV0aW9uJHBhclsyXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gYWJzKHNvbHV0aW9uJHBhclszXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFfZ2FpbiA9IGFicyhzb2x1dGlvbiRwYXJbNF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2xvc3MgPSBhYnMoc29sdXRpb24kcGFyWzVdKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCiMgLSBwbG90IHByZWRpY3Rpb25zCmdncGxvdChkYXRhID0gbWVxX2RhdGEsCiAgICAgICBhZXMoeCA9IG1lcSwgeSA9IGNwdF9tZXEpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNpemUgPSAuMjUsIGNvbG9yID0gInJlZCIsIHNlID0gRkFMU0UpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJibGFjayIpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIGdndGl0bGUoIkNQVCIpICsKICB4bGFiKCJQcmVkaWN0ZWQgTUVxIikgKyAKICB5bGFiKCJPYnNlcnZlZCBNRXEiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0uNSwgc2l6ZSA9IDEwKSkKYGBgCgpSdW4gMSwwMDAgb3B0aW1pemF0aW9ucyAoaW4gcGFyYWxsZWwsIHVzaW5nIGBzbm93ZmFsbGApOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gaW5pdCBjbG91ZApzZkluaXQocGFyYWxsZWwgPSBUUlVFLCBjcHVzID0gMjMpCgojIC0gZXhwb3J0CnNmRXhwb3J0KCJtZXFfZGF0YSIpCnNmRXhwb3J0KCJjcHRfbWxlIikKc2ZFeHBvcnQoImNwdF9wcmVkaWN0IikKc2ZFeHBvcnQoImNwdF9saWtlbGlob29kIikKc2ZFeHBvcnQoInBfd2VpZ2h0IikKc2ZFeHBvcnQoInJkX3V0aWxpdHlfcG93ZXIiKQoKIyAtIG1hbnkgb3B0aW1pemF0aW9uIHJ1bnMKaW5pdF9wYXJzIDwtIGxhcHBseSgxOjEwMDAsIGZ1bmN0aW9uKHgpIHsKICByZXR1cm4oCiAgICBsaXN0KAogICAgICBpbml0X3Job19nID0gcnVuaWYoMSwgMCwgMSksCiAgICAgIGluaXRfcmhvX2wgPSBydW5pZigxLCAwLCAxKSwKICAgICAgaW5pdF9sYW1iZGEgPSBydW5pZigxLCAwLCAxKSwKICAgICAgaW5pdF9nYW1tYV9nYWluID0gcnVuaWYoMSwgMCwgMSksCiAgICAgIGluaXRfZ2FtbWFfbG9zcyA9IHJ1bmlmKDEsIDAsIDEpLAogICAgICBpbml0X3NpZ21hID0gcnVuaWYoMSwgMCwgMSkKICAgICkKICApCn0pCgoKIyAtIHJ1bgpyZXN1bHRzIDwtIHNmQ2x1c3RlckFwcGx5TEIoaW5pdF9wYXJzLCBmdW5jdGlvbih4KSB7CiAgCiAgaW5pdF9yaG9fZyA8LSB4JGluaXRfcmhvX2cKICBpbml0X3Job19sID0geCRpbml0X3Job19sCiAgaW5pdF9sYW1iZGEgPSB4JGluaXRfbGFtYmRhCiAgaW5pdF9nYW1tYV9nYWluID0geCRpbml0X2dhbW1hX2dhaW4KICBpbml0X2dhbW1hX2xvc3MgPSB4JGluaXRfZ2FtbWFfbG9zcwogIGluaXRfc2lnbWEgPC0geCRpbml0X3NpZ21hCiAgICAgICAgCiAgIyAtIG9wdGltaXphdGlvbiBydW4KICBvcHRpbV9zb2wgPC0gdHJ5Q2F0Y2gob3B0aW0ocGFyID0gYyhpbml0X3Job19nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRfcmhvX2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdF9sYW1iZGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdF9nYW1tYV9nYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRfZ2FtbWFfbG9zcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0X3NpZ21hKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm4gPSBjcHRfbWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiTmVsZGVyLU1lYWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gbGlzdCgibWF4aXQiID0gMWU1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVsdG9sIiA9IDFlLTE0KSksCiAgICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gZnVuY3Rpb24oY29uZGl0aW9uKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKE5VTEwpCiAgICAgICAgICAgICAgICAgICAgICAgIH0pCiAgCiAgaWYgKCFpcy5udWxsKG9wdGltX3NvbCkpIHsKICAgIAogICAgIyAtIHByZWRpY3Rpb25zCiAgICBwcmVkcyA8LSBjcHRfcHJlZGljdChtZXFfZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICByaG9fZyA9IGFicyhvcHRpbV9zb2wkcGFyWzFdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHJob19sID0gYWJzKG9wdGltX3NvbCRwYXJbMl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgbGFtYmRhID0gYWJzKG9wdGltX3NvbCRwYXJbM10pLAogICAgICAgICAgICAgICAgICAgICAgICAgZ2FtbWFfZ2FpbiA9IGFicyhvcHRpbV9zb2wkcGFyWzRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2xvc3MgPSBhYnMob3B0aW1fc29sJHBhcls1XSkKICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgIAogICAgcmV0dXJuKAogICAgICBsaXN0KHJob19nID0gYWJzKG9wdGltX3NvbCRwYXJbMV0pLAogICAgICAgICAgIHJob19sID0gYWJzKG9wdGltX3NvbCRwYXJbMl0pLAogICAgICAgICAgIGxhbWJkYSA9IGFicyhvcHRpbV9zb2wkcGFyWzNdKSwKICAgICAgICAgICBnYW1tYV9nYWluID0gYWJzKG9wdGltX3NvbCRwYXJbNF0pLAogICAgICAgICAgIGdhbW1hX2xvc3MgPSBhYnMob3B0aW1fc29sJHBhcls1XSksCiAgICAgICAgICAgc2lnbWEgPSBhYnMob3B0aW1fc29sJHBhcls2XSksCiAgICAgICAgICAgbmxsID0gb3B0aW1fc29sJHZhbHVlLAogICAgICAgICAgIGNvbnZlcmdlbmNlID0gb3B0aW1fc29sJGNvbnZlcmdlbmNlLAogICAgICAgICAgIHByZWRpY3Rpb25zID0gcHJlZHMpCiAgICAgICkKICAgIAogIH0gZWxzZSB7CiAgICAgIAogICAgICByZXR1cm4oCiAgICAgIGxpc3QocmhvID0gTlVMTCwKICAgICAgICAgICBzaWdtYSA9IE5VTEwsCiAgICAgICAgICAgbmxsID0gTlVMTCwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IE5VTEwsCiAgICAgICAgICAgcHJlZGljdGlvbnMgPSBOVUxMKQogICAgICApCiAgICAKICB9CiAgCiAgCn0pCgojIC0gc3RvcCBjbHVzdGVyCnNmU3RvcCgpCgojIC0gZmlsdGVyIG91dCBwb3NzaWJsZSBkZWdlbmVyYXRpdmUgTmVsZGVyLU1lYWQgc29sdXRpb25zCmNvbnZfbGlzdCA8LSBzYXBwbHkocmVzdWx0cywgZnVuY3Rpb24oeCkgeCRjb252ZXJnZW5jZSkKZGVnZW5lcmF0ZSA8LSB3aGljaChjb252X2xpc3QgIT0gMCkKbmxsX2xpc3QgPC0gbmxsX2xpc3RbLWRlZ2VuZXJhdGVdCiMgLSBmaW5kIGJlc3Qgc29sdXRpb24KbmxsX2xpc3QgPC0gc2FwcGx5KHJlc3VsdHMsIGZ1bmN0aW9uKHgpIHgkbmxsKQpiZXN0X3J1biA8LSB3aGljaC5taW4obmxsX2xpc3QpWzFdCnByaW50KHBhc3RlMCgiQ2hlY2sgY29udmVyZ2VuY2U6ICIsIHJlc3VsdHNbW2Jlc3RfcnVuXV0kY29udmVyZ2VuY2UpKQplc3RpbWF0ZWRfcmhvX2cgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRyaG9fZwpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gZm9yIGdhaW5zOiAiLCByb3VuZChlc3RpbWF0ZWRfcmhvX2csMikpKQplc3RpbWF0ZWRfcmhvX2wgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRyaG9fbApwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gZm9yIGxvc3NlczogIiwgcm91bmQoZXN0aW1hdGVkX3Job19sLDIpKSkKZXN0aW1hdGVkX2xhbWJkYSA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJGxhbWJkYQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBMYW1iZGEgKGxvc3MgYXZlcnNpb24pOiAiLCByb3VuZChlc3RpbWF0ZWRfbGFtYmRhLDIpKSkKZXN0aW1hdGVkX2dhbW1hX2dhaW4gPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRnYW1tYV9nYWluCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIEdhbW1hIGZvciBnYWluczogIiwgcm91bmQoZXN0aW1hdGVkX2dhbW1hX2dhaW4sMikpKQplc3RpbWF0ZWRfZ2FtbWFfbG9zcyA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJGdhbW1hX2xvc3MKcHJpbnQocGFzdGUwKCJFc3RpbWF0ZWQgR2FtbWEgZm9yIGxvc3NlczogIiwgcm91bmQoZXN0aW1hdGVkX2dhbW1hX2xvc3MsMikpKQplc3RpbWF0ZWRfc2lnbWEgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRzaWdtYQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBTaWdtYTogIiwgcm91bmQoZXN0aW1hdGVkX3NpZ21hLDIpKSkKbmxsIDwtIHJlc3VsdHNbW2Jlc3RfcnVuXV0kbmxsCnByaW50KHBhc3RlMCgiTmVnYXRpdmUgTG9nTGlrZWxpaG9vZDogIiwgcm91bmQobmxsLDIpKSkKb3B0aW1hbF9wcmVkaWN0aW9ucyA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJHByZWRpY3Rpb25zCgojIC0gcGxvdCBwcmVkaWN0aW9ucwpwbG90X2ZyYW1lIDwtIG1lcV9kYXRhCm1lcV9kYXRhJGNwdF9tZXEgPC0gb3B0aW1hbF9wcmVkaWN0aW9ucwpnZ3Bsb3QoZGF0YSA9IG1lcV9kYXRhLAogICAgICAgYWVzKHggPSBtZXEsIHkgPSBjcHRfbWVxKSkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzaXplID0gLjI1LCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCJDUFQiKSArCiAgeGxhYigiT2JzZXJ2ZWQgTUVxIikgKyAKICB5bGFiKCJQcmVkaWN0ZWQgTUVxIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKIyMgUFQgb3B0aW1pemF0aW9uIGZvciB0aGUgS1Q5MiBkYXRhIHNldAoKKipOT1RFLioqIFRoZXJlIGFyZSBubyBtaXhlZC1wcm9zcGVjdHMgaW4gdGhlIGBrdDkyLmNzdmAgZGF0YSBzZXQ6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSBsb2FkIHNvbWUgTUVxIGV4cGVyaW1lbnRhbCBkYXRhCm1lcV9kYXRhIDwtIHJlYWQuY3N2KHBhc3RlMChrdDkyX2RhdGFfZGlyLCAia3Q5Mi5jc3YiKSwKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQojIC0gcGVyY2VudHMgdG8gcHJvYmFiaWxpdHkgaW4gbWVxX2RhdGEKbWVxX2RhdGEkcDEgPC0gbWVxX2RhdGEkcDEvMTAwCm1lcV9kYXRhJHAyIDwtIG1lcV9kYXRhJHAyLzEwMApwcmludChtZXFfZGF0YSkKYGBgCgpSdW4gMSwwMDAgb3B0aW1pemF0aW9ucyBmb3IgdGhlIGBLVDkyYCBkYXRhIHNldDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIGluaXQgY2xvdWQKc2ZJbml0KHBhcmFsbGVsID0gVFJVRSwgY3B1cyA9IDIzKQoKIyAtIGV4cG9ydApzZkV4cG9ydCgibWVxX2RhdGEiKQpzZkV4cG9ydCgiY3B0X21sZSIpCnNmRXhwb3J0KCJjcHRfcHJlZGljdCIpCnNmRXhwb3J0KCJjcHRfbGlrZWxpaG9vZCIpCnNmRXhwb3J0KCJwX3dlaWdodCIpCnNmRXhwb3J0KCJyZF91dGlsaXR5X3Bvd2VyIikKCiMgLSBtYW55IG9wdGltaXphdGlvbiBydW5zCmluaXRfcGFycyA8LSBsYXBwbHkoMToxMDAwLCBmdW5jdGlvbih4KSB7CiAgcmV0dXJuKAogICAgbGlzdCgKICAgICAgaW5pdF9yaG9fZyA9IHJ1bmlmKDEsIDAsIDEpLAogICAgICBpbml0X3Job19sID0gcnVuaWYoMSwgMCwgMSksCiAgICAgIGluaXRfbGFtYmRhID0gcnVuaWYoMSwgMCwgMSksCiAgICAgIGluaXRfZ2FtbWFfZ2FpbiA9IHJ1bmlmKDEsIDAsIDEpLAogICAgICBpbml0X2dhbW1hX2xvc3MgPSBydW5pZigxLCAwLCAxKSwKICAgICAgaW5pdF9zaWdtYSA9IHJ1bmlmKDEsIDAsIDEpCiAgICApCiAgKQp9KQoKCiMgLSBydW4KcmVzdWx0cyA8LSBzZkNsdXN0ZXJBcHBseUxCKGluaXRfcGFycywgZnVuY3Rpb24oeCkgewogIAogIGluaXRfcmhvX2cgPC0geCRpbml0X3Job19nCiAgaW5pdF9yaG9fbCA9IHgkaW5pdF9yaG9fbAogIGluaXRfbGFtYmRhID0geCRpbml0X2xhbWJkYQogIGluaXRfZ2FtbWFfZ2FpbiA9IHgkaW5pdF9nYW1tYV9nYWluCiAgaW5pdF9nYW1tYV9sb3NzID0geCRpbml0X2dhbW1hX2xvc3MKICBpbml0X3NpZ21hIDwtIHgkaW5pdF9zaWdtYQogICAgICAgIAogICMgLSBvcHRpbWl6YXRpb24gcnVuCiAgb3B0aW1fc29sIDwtIHRyeUNhdGNoKG9wdGltKHBhciA9IGMoaW5pdF9yaG9fZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0X3Job19sLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRfbGFtYmRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluaXRfZ2FtbWFfZ2FpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbml0X2dhbW1hX2xvc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdF9zaWdtYSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZuID0gY3B0X21sZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIk5lbGRlci1NZWFkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3QoIm1heGl0IiA9IDFlNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlbHRvbCIgPSAxZS0xNCkpLAogICAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGNvbmRpdGlvbikgewogICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybihOVUxMKQogICAgICAgICAgICAgICAgICAgICAgICB9KQogIAogIGlmICghaXMubnVsbChvcHRpbV9zb2wpKSB7CiAgICAKICAgICMgLSBwcmVkaWN0aW9ucwogICAgcHJlZHMgPC0gY3B0X3ByZWRpY3QobWVxX2RhdGEsIAogICAgICAgICAgICAgICAgICAgICAgICAgcmhvX2cgPSBhYnMob3B0aW1fc29sJHBhclsxXSksCiAgICAgICAgICAgICAgICAgICAgICAgICByaG9fbCA9IGFicyhvcHRpbV9zb2wkcGFyWzJdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYSA9IGFicyhvcHRpbV9zb2wkcGFyWzNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGdhbW1hX2dhaW4gPSBhYnMob3B0aW1fc29sJHBhcls0XSksCiAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYV9sb3NzID0gYWJzKG9wdGltX3NvbCRwYXJbNV0pCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAKICAgIHJldHVybigKICAgICAgbGlzdChyaG9fZyA9IGFicyhvcHRpbV9zb2wkcGFyWzFdKSwKICAgICAgICAgICByaG9fbCA9IGFicyhvcHRpbV9zb2wkcGFyWzJdKSwKICAgICAgICAgICBsYW1iZGEgPSBhYnMob3B0aW1fc29sJHBhclszXSksCiAgICAgICAgICAgZ2FtbWFfZ2FpbiA9IGFicyhvcHRpbV9zb2wkcGFyWzRdKSwKICAgICAgICAgICBnYW1tYV9sb3NzID0gYWJzKG9wdGltX3NvbCRwYXJbNV0pLAogICAgICAgICAgIHNpZ21hID0gYWJzKG9wdGltX3NvbCRwYXJbNl0pLAogICAgICAgICAgIG5sbCA9IG9wdGltX3NvbCR2YWx1ZSwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IG9wdGltX3NvbCRjb252ZXJnZW5jZSwKICAgICAgICAgICBwcmVkaWN0aW9ucyA9IHByZWRzKQogICAgICApCiAgICAKICB9IGVsc2UgewogICAgICAKICAgICAgcmV0dXJuKAogICAgICBsaXN0KHJob19nID0gTlVMTCwKICAgICAgICAgICByaG9fbCA9IE5VTEwsCiAgICAgICAgICAgbGFtYmRhID0gTlVMTCwKICAgICAgICAgICBnYW1tYV9nYWluID0gTlVMTCwKICAgICAgICAgICBnYW1tYV9sb3NzID0gTlVMTCwKICAgICAgICAgICBzaWdtYSA9IE5VTEwsCiAgICAgICAgICAgbmxsID0gTlVMTCwKICAgICAgICAgICBjb252ZXJnZW5jZSA9IE5VTEwsCiAgICAgICAgICAgcHJlZGljdGlvbnMgPSBOVUxMKQogICAgICApCiAgICAKICB9CiAgCiAgCn0pCgojIC0gc3RvcCBjbHVzdGVyCnNmU3RvcCgpCgojIC0gZmlsdGVyIG91dCBwb3NzaWJsZSBkZWdlbmVyYXRpdmUgTmVsZGVyLU1lYWQgc29sdXRpb25zCmNvbnZfbGlzdCA8LSBzYXBwbHkocmVzdWx0cywgZnVuY3Rpb24oeCkgeCRjb252ZXJnZW5jZSkKZGVnZW5lcmF0ZSA8LSB3aGljaChjb252X2xpc3QgIT0gMCkKbmxsX2xpc3QgPC0gbmxsX2xpc3RbLWRlZ2VuZXJhdGVdCiMgLSBmaW5kIGJlc3Qgc29sdXRpb24KbmxsX2xpc3QgPC0gc2FwcGx5KHJlc3VsdHMsIGZ1bmN0aW9uKHgpIHgkbmxsKQpiZXN0X3J1biA8LSB3aGljaC5taW4obmxsX2xpc3QpWzFdCnByaW50KHBhc3RlMCgiQ2hlY2sgY29udmVyZ2VuY2U6ICIsIHJlc3VsdHNbW2Jlc3RfcnVuXV0kY29udmVyZ2VuY2UpKQplc3RpbWF0ZWRfcmhvX2cgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRyaG9fZwpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gZm9yIGdhaW5zOiAiLCByb3VuZChlc3RpbWF0ZWRfcmhvX2csMikpKQplc3RpbWF0ZWRfcmhvX2wgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRyaG9fbApwcmludChwYXN0ZTAoIkVzdGltYXRlZCBSaG8gZm9yIGxvc3NlczogIiwgcm91bmQoZXN0aW1hdGVkX3Job19sLDIpKSkKZXN0aW1hdGVkX2xhbWJkYSA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJGxhbWJkYQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBMYW1iZGEgKGxvc3MgYXZlcnNpb24pOiAiLCByb3VuZChlc3RpbWF0ZWRfbGFtYmRhLDIpKSkKZXN0aW1hdGVkX2dhbW1hX2dhaW4gPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRnYW1tYV9nYWluCnByaW50KHBhc3RlMCgiRXN0aW1hdGVkIEdhbW1hIGZvciBnYWluczogIiwgcm91bmQoZXN0aW1hdGVkX2dhbW1hX2dhaW4sMikpKQplc3RpbWF0ZWRfZ2FtbWFfbG9zcyA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJGdhbW1hX2xvc3MKcHJpbnQocGFzdGUwKCJFc3RpbWF0ZWQgR2FtbWEgZm9yIGxvc3NlczogIiwgcm91bmQoZXN0aW1hdGVkX2dhbW1hX2xvc3MsMikpKQplc3RpbWF0ZWRfc2lnbWEgPC0gcmVzdWx0c1tbYmVzdF9ydW5dXSRzaWdtYQpwcmludChwYXN0ZTAoIkVzdGltYXRlZCBTaWdtYTogIiwgcm91bmQoZXN0aW1hdGVkX3NpZ21hLDIpKSkKbmxsIDwtIHJlc3VsdHNbW2Jlc3RfcnVuXV0kbmxsCnByaW50KHBhc3RlMCgiTmVnYXRpdmUgTG9nTGlrZWxpaG9vZDogIiwgcm91bmQobmxsLDIpKSkKb3B0aW1hbF9wcmVkaWN0aW9ucyA8LSByZXN1bHRzW1tiZXN0X3J1bl1dJHByZWRpY3Rpb25zCgojIC0gcGxvdCBwcmVkaWN0aW9ucwpwbG90X2ZyYW1lIDwtIG1lcV9kYXRhCm1lcV9kYXRhJGNwdF9tZXEgPC0gb3B0aW1hbF9wcmVkaWN0aW9ucwpnZ3Bsb3QoZGF0YSA9IG1lcV9kYXRhLAogICAgICAgYWVzKHggPSBtZXEsIHkgPSBjcHRfbWVxKSkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzaXplID0gLjI1LCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAiYmxhY2siKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKyAKICBnZ3RpdGxlKCJDUFQiKSArCiAgeGxhYigiT2JzZXJ2ZWQgTUVxIikgKyAKICB5bGFiKCJQcmVkaWN0ZWQgTUVxIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9LjUsIHNpemUgPSAxMCkpCmBgYAoKKipOT1RFLioqICRcbGFtYmRhJCwgdGhlIExvc3MgQXZlcnNpb24gcGFyYW1ldGVyLCBjYW5ub3QgYmUgZXN0aW1hdGVkIGZyb20gZXhwZXJpbWVudGFsIGRlc2lnbnMgdGhhdCBkbyBub3QgZW5jb21wYXNzICoqbWl4ZWQgbG90dGVyaWVzKiouCgojIyBNb2RlbCBzZWxlY3Rpb246IHBsYW4gZm9yIEstZm9sZCBDVgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gbG9hZCBzb21lIE1FcSBleHBlcmltZW50YWwgZGF0YQptZXFfZGF0YSA8LSByZWFkLmNzdihwYXN0ZTAobWVxX2RhdGFfZGlyLCAiZXM0djEuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKIyAtIHBlcmNlbnRzIHRvIHByb2JhYmlsaXR5IGluIG1lcV9kYXRhCm1lcV9kYXRhJHAxIDwtIG1lcV9kYXRhJHAxLzEwMAptZXFfZGF0YSRwMiA8LSBtZXFfZGF0YSRwMi8xMDAKcHJpbnQobWVxX2RhdGEpCmBgYAoKIyMjIFR5cGVzIG9mIGxvdHRlcmllcwoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIC0gdHlwZXMgb2YgbG90dGVyaWVzCiMgLSBzdHJpY3RseSBwb3NpdGl2ZQpwb3NfbG90dCA8LSB3aGljaChtZXFfZGF0YSR2MSA+IDAgJiBtZXFfZGF0YSR2MiA+IDApCnByaW50KHBhc3RlMCgiVGhlcmUgYXJlICIsIGxlbmd0aChwb3NfbG90dCksICIgc3RyaWN0bHkgUE9TSVRJVkUgbG90dGVyaWVzLiIpKQojIC0gbm9uLW5lZ2F0aXZlCm5uZWdfbG90dCA8LSB3aGljaCAoIAogIChtZXFfZGF0YSR2MSA+IDAgJiBtZXFfZGF0YSR2MiA9PSAwKSB8CiAgICAobWVxX2RhdGEkdjEgPT0gMCAmIG1lcV9kYXRhJHYyID4gMCkKKQpwcmludChwYXN0ZTAoIlRoZXJlIGFyZSAiLCBsZW5ndGgobm5lZ19sb3R0KSwgIiBOT04tTkVHQVRJVkUgbG90dGVyaWVzLiIpKQojIC0gc3RyaWN0bHkgbmVnYXRpdmUKbmVnX2xvdHQgPC0gd2hpY2gobWVxX2RhdGEkdjEgPCAwICYgbWVxX2RhdGEkdjIgPCAwKQpwcmludChwYXN0ZTAoIlRoZXJlIGFyZSAiLCBsZW5ndGgobmVnX2xvdHQpLCAiIHN0cmljdGx5IE5FR0FUSVZFIGxvdHRlcmllcy4iKSkKIyAtIG5vbi1wb3NpdGl2ZQpucG9zX2xvdHQgPC0gd2hpY2ggKCAKICAobWVxX2RhdGEkdjEgPCAwICYgbWVxX2RhdGEkdjIgPT0gMCkgfAogICAgKG1lcV9kYXRhJHYxID09IDAgJiBtZXFfZGF0YSR2MiA8IDApCikKcHJpbnQocGFzdGUwKCJUaGVyZSBhcmUgIiwgbGVuZ3RoKG5wb3NfbG90dCksICIgTk9OLVBPU0lUSVZFIGxvdHRlcmllcy4iKSkKIyAtIG1peGVkCm1peGVkX2xvdHQgPC0gd2hpY2ggKCAKICAobWVxX2RhdGEkdjEgPCAwICYgbWVxX2RhdGEkdjIgPiAwKSB8CiAgICAobWVxX2RhdGEkdjEgPiAwICYgbWVxX2RhdGEkdjIgPCAwKQopCnByaW50KHBhc3RlMCgiVGhlcmUgYXJlICIsIGxlbmd0aChtaXhlZF9sb3R0KSwgIiBNSVhFRCBsb3R0ZXJpZXMuIikpCgpgYGAKCkxldCdzIGVudGVyIGB0eXBlYCB0byBgbWVxX2RhdGFgOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQptZXFfZGF0YSR0eXBlIDwtIGFwcGx5KG1lcV9kYXRhLCAxLCBmdW5jdGlvbih4KSB7CiAgaWYgKHhbMl0gPiAwICYgeFs0XSA+IDApIHsKICAgIHJldHVybigicCIpCiAgfSBlbHNlIGlmICgoeFsyXSA+IDAgJiB4WzRdID09IDApIHwgCiAgICAgICAgICAgICAoeFsyXSA9PSAwICYgeFs0XSA+IDApKSB7CiAgICByZXR1cm4oIm5uIikKICB9IGVsc2UgaWYgKHhbMl0gPCAwICYgeFs0XSA8IDApIHsKICAgIHJldHVybigibiIpCiAgfSBlbHNlIGlmICgoeFsyXSA8IDAgJiB4WzRdID09IDApIHwKICAgICh4WzJdID09IDAgJiB4WzRdIDwgMCkpIHsKICAgIHJldHVybigibnAiKQogIH0gZWxzZSB7CiAgICByZXR1cm4oIm1peGVkIikKICB9Cn0pCnRhYmxlKG1lcV9kYXRhJHR5cGUpCmBgYAoKWW91IG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgYWxsIHR5cGVzIG9mIGxvdHRlcmllcyBlbnRlciBhbGwgb2YgeW91ciBmb2xkcyBpbiBrLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBvZiB0aGlzIG1vZGVsLgoKIyMjIEZvbGRzIHBsYW4KCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWVxX2RhdGEkZm9sZCA8LSAwCnR5cGVzIDwtIHVuaXF1ZShtZXFfZGF0YSR0eXBlKQpmb3IgKGkgaW4gMTpsZW5ndGgodHlwZXMpKSB7CiAgdyA8LSB3aGljaChtZXFfZGF0YSR0eXBlID09IHR5cGVzW2ldKQogIGZfYXNnbiA8LSByZXAoMTo1LCBsZW5ndGgodykvNSkKICBmX2FzZ24gPC0gc2FtcGxlKGZfYXNnbiwgc2l6ZSA9IGxlbmd0aChmX2FzZ24pLCByZXBsYWNlID0gRkFMU0UpCiAgbWVxX2RhdGEkZm9sZFt3XSA8LSBmX2FzZ24KfQpwcmludChtZXFfZGF0YSkKYGBgCgpDaGVjazoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KdGFibGUobWVxX2RhdGEkZm9sZCwgbWVxX2RhdGEkdHlwZSkKYGBgCgoqKioKR29yYW4gUy4gTWlsb3Zhbm92acSHCgpEYXRhS29sZWt0aXYsIDIwMjIuCgpjb250YWN0OiBnb3Jhbi5taWxvdmFub3ZpY0BkYXRha29sZWt0aXYuY29tCgohW10oX2ltZy9ES19Mb2dvXzEwMC5wbmcpCgoqKioKTGljZW5zZTogW0dQTHYzXShodHRwOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvZ3BsLTMuMC50eHQpClRoaXMgTm90ZWJvb2sgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQgYW5kL29yIG1vZGlmeSBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieSB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvciAoYXQgeW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgpUaGlzIE5vdGVib29rIGlzIGRpc3RyaWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsIGJ1dCBXSVRIT1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0YWlscy4KWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgYWxvbmcgd2l0aCB0aGlzIE5vdGVib29rLiBJZiBub3QsIHNlZSA8aHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzLz4uCgoqKioKCg==